1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <sal/config.h>
21 #include <sal/log.hxx>
22 #include <osl/diagnose.h>
23 
24 #include <condition_variable>
25 #include <mutex>
26 #include <utility>
27 
28 #include <config_features.h>
29 
30 #include <stdio.h>
31 
32 #include <comphelper/solarmutex.hxx>
33 
34 #include <comphelper/lok.hxx>
35 
36 #include <osl/process.h>
37 
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>
47 
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>
60 
61 #include <print.h>
62 
63 #include <comphelper/processfactory.hxx>
64 
65 #include <com/sun/star/uri/ExternalUriReferenceTranslator.hpp>
66 #include <com/sun/star/uno/XComponentContext.hpp>
67 
68 #include <premac.h>
69 #include <Foundation/Foundation.h>
70 #include <ApplicationServices/ApplicationServices.h>
71 #import "apple_remote/RemoteMainController.h"
72 #include <apple_remote/RemoteControl.h>
73 #include <postmac.h>
74 
75 extern "C" {
76 #include <crt_externs.h>
77 }
78 
79 using namespace std;
80 using namespace ::com::sun::star;
81 
82 static int* gpnInit = nullptr;
83 static NSMenu* pDockMenu = nil;
84 static bool bLeftMain = false;
85 
86 class AquaDelayedSettingsChanged : public Idle
87 {
88     bool            mbInvalidate;
89 
90 public:
AquaDelayedSettingsChanged(bool bInvalidate)91     AquaDelayedSettingsChanged( bool bInvalidate ) :
92         mbInvalidate( bInvalidate )
93     {
94     }
95 
Invoke()96     virtual void Invoke() override
97     {
98         AquaSalInstance *pInst = GetSalData()->mpInstance;
99         SalFrame *pAnyFrame = pInst->anyFrame();
100         if( pAnyFrame )
101             pAnyFrame->CallCallback( SalEvent::SettingsChanged, nullptr );
102 
103         if( mbInvalidate )
104         {
105             for( auto pSalFrame : pInst->getFrames() )
106             {
107                 AquaSalFrame* pFrame = static_cast<AquaSalFrame*>( pSalFrame );
108                 if( pFrame->mbShown )
109                     pFrame->SendPaintEvent();
110             }
111         }
112         delete this;
113     }
114 };
115 
delayedSettingsChanged(bool bInvalidate)116 void AquaSalInstance::delayedSettingsChanged( bool bInvalidate )
117 {
118     osl::Guard< comphelper::SolarMutex > aGuard( *GetYieldMutex() );
119     AquaDelayedSettingsChanged* pIdle = new AquaDelayedSettingsChanged( bInvalidate );
120     pIdle->SetDebugName( "AquaSalInstance AquaDelayedSettingsChanged" );
121     pIdle->Start();
122 }
123 
124 // the std::list<const ApplicationEvent*> must be available before any SalData/SalInst/etc. objects are ready
125 std::list<const ApplicationEvent*> AquaSalInstance::aAppEventList;
126 
GetDynamicDockMenu()127 NSMenu* AquaSalInstance::GetDynamicDockMenu()
128 {
129     if( ! pDockMenu && ! bLeftMain )
130         pDockMenu = [[NSMenu alloc] initWithTitle: @""];
131     return pDockMenu;
132 }
133 
isOnCommandLine(const OUString & rArg)134 bool AquaSalInstance::isOnCommandLine( const OUString& rArg )
135 {
136     sal_uInt32 nArgs = osl_getCommandArgCount();
137     for( sal_uInt32 i = 0; i < nArgs; i++ )
138     {
139         OUString aArg;
140         osl_getCommandArg( i, &aArg.pData );
141         if( aArg.equals( rArg ) )
142             return true;
143     }
144     return false;
145 }
146 
AfterAppInit()147 void AquaSalInstance::AfterAppInit()
148 {
149     [[NSNotificationCenter defaultCenter] addObserver: NSApp
150                                           selector: @selector(systemColorsChanged:)
151                                           name: NSSystemColorsDidChangeNotification
152                                           object: nil ];
153     [[NSNotificationCenter defaultCenter] addObserver: NSApp
154                                           selector: @selector(screenParametersChanged:)
155                                           name: NSApplicationDidChangeScreenParametersNotification
156                                           object: nil ];
157     // add observers for some settings changes that affect vcl's settings
158     // scrollbar variant
159     [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp
160                                           selector: @selector(scrollbarVariantChanged:)
161                                           name: @"AppleAquaScrollBarVariantChanged"
162                                           object: nil ];
163     // scrollbar page behavior ("jump to here" or not)
164     [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp
165                                           selector: @selector(scrollbarSettingsChanged:)
166                                           name: @"AppleNoRedisplayAppearancePreferenceChanged"
167                                           object: nil ];
168 #if !HAVE_FEATURE_MACOSX_SANDBOX
169     // Initialize Apple Remote
170     GetSalData()->mpAppleRemoteMainController = [[AppleRemoteMainController alloc] init];
171 
172     [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp
173                                            selector: @selector(applicationWillBecomeActive:)
174                                            name: @"AppleRemoteWillBecomeActive"
175                                            object: nil ];
176 
177     [[NSDistributedNotificationCenter defaultCenter] addObserver: NSApp
178                                            selector: @selector(applicationWillResignActive:)
179                                            name: @"AppleRemoteWillResignActive"
180                                            object: nil ];
181 #endif
182 }
183 
SalAbort(const OUString & rErrorText,bool bDumpCore)184 void SalAbort( const OUString& rErrorText, bool bDumpCore )
185 {
186     if( rErrorText.isEmpty() )
187         fprintf( stderr, "Application Error " );
188     else
189         fprintf( stderr, "%s ",
190             OUStringToOString( rErrorText, osl_getThreadTextEncoding() ).getStr() );
191     if( bDumpCore )
192         abort();
193     else
194         _exit(1);
195 }
196 
SalYieldMutex()197 SalYieldMutex::SalYieldMutex()
198     : m_aCodeBlock( nullptr )
199 {
200 }
201 
~SalYieldMutex()202 SalYieldMutex::~SalYieldMutex()
203 {
204 }
205 
doAcquire(sal_uInt32 nLockCount)206 void SalYieldMutex::doAcquire( sal_uInt32 nLockCount )
207 {
208     AquaSalInstance *pInst = GetSalData()->mpInstance;
209     if ( pInst && pInst->IsMainThread() )
210     {
211         if ( pInst->mbNoYieldLock )
212             return;
213         do {
214             RuninmainBlock block = nullptr;
215             {
216                 std::unique_lock<std::mutex> g(m_runInMainMutex);
217                 if (m_aMutex.tryToAcquire()) {
218                     assert(m_aCodeBlock == nullptr);
219                     m_wakeUpMain = false;
220                     break;
221                 }
222                 // wait for doRelease() or RUNINMAIN_* to set the condition
223                 m_aInMainCondition.wait(g, [this]() { return m_wakeUpMain; });
224                 m_wakeUpMain = false;
225                 std::swap(block, m_aCodeBlock);
226             }
227             if ( block )
228             {
229                 assert( !pInst->mbNoYieldLock );
230                 pInst->mbNoYieldLock = true;
231                 block();
232                 pInst->mbNoYieldLock = false;
233                 Block_release( block );
234                 std::scoped_lock<std::mutex> g(m_runInMainMutex);
235                 assert(!m_resultReady);
236                 m_resultReady = true;
237                 m_aResultCondition.notify_all();
238             }
239         }
240         while ( true );
241     }
242     else
243         m_aMutex.acquire();
244     ++m_nCount;
245     --nLockCount;
246 
247     comphelper::SolarMutex::doAcquire( nLockCount );
248 }
249 
doRelease(const bool bUnlockAll)250 sal_uInt32 SalYieldMutex::doRelease( const bool bUnlockAll )
251 {
252     AquaSalInstance *pInst = GetSalData()->mpInstance;
253     if ( pInst->mbNoYieldLock && pInst->IsMainThread() )
254         return 1;
255     sal_uInt32 nCount;
256     {
257         std::scoped_lock<std::mutex> g(m_runInMainMutex);
258         // read m_nCount before doRelease
259         bool const isReleased(bUnlockAll || m_nCount == 1);
260         nCount = comphelper::SolarMutex::doRelease( bUnlockAll );
261         if (isReleased && !pInst->IsMainThread()) {
262             m_wakeUpMain = true;
263             m_aInMainCondition.notify_all();
264         }
265     }
266     return nCount;
267 }
268 
IsCurrentThread() const269 bool SalYieldMutex::IsCurrentThread() const
270 {
271     if ( !GetSalData()->mpInstance->mbNoYieldLock )
272         return comphelper::SolarMutex::IsCurrentThread();
273     else
274         return GetSalData()->mpInstance->IsMainThread();
275 }
276 
277 // some convenience functions regarding the yield mutex, aka solar mutex
278 
ImplSalYieldMutexTryToAcquire()279 bool ImplSalYieldMutexTryToAcquire()
280 {
281     AquaSalInstance* pInst = GetSalData()->mpInstance;
282     if ( pInst )
283         return pInst->GetYieldMutex()->tryToAcquire();
284     else
285         return FALSE;
286 }
287 
ImplSalYieldMutexRelease()288 void ImplSalYieldMutexRelease()
289 {
290     AquaSalInstance* pInst = GetSalData()->mpInstance;
291     if ( pInst )
292         pInst->GetYieldMutex()->release();
293 }
294 
295 extern "C" {
create_SalInstance()296 VCLPLUG_OSX_PUBLIC SalInstance* create_SalInstance()
297 {
298     SalData* pSalData = new SalData;
299 
300     NSAutoreleasePool * pool = [ [ NSAutoreleasePool alloc ] init ];
301     unlink([[NSString stringWithFormat:@"%@/Library/Saved Application State/%s.savedState/restorecount.plist", NSHomeDirectory(), MACOSX_BUNDLE_IDENTIFIER] UTF8String]);
302     unlink([[NSString stringWithFormat:@"%@/Library/Saved Application State/%s.savedState/restorecount.txt", NSHomeDirectory(), MACOSX_BUNDLE_IDENTIFIER] UTF8String]);
303     [ pool drain ];
304 
305     // create our cocoa NSApplication
306     [VCL_NSApplication sharedApplication];
307 
308     SalData::ensureThreadAutoreleasePool();
309 
310     // put cocoa into multithreaded mode
311     [NSThread detachNewThreadSelector:@selector(enableCocoaThreads:) toTarget:[[CocoaThreadEnabler alloc] init] withObject:nil];
312 
313     // activate our delegate methods
314     [NSApp setDelegate: NSApp];
315 
316     SAL_WARN_IF( pSalData->mpInstance != nullptr, "vcl", "more than one instance created" );
317     AquaSalInstance* pInst = new AquaSalInstance;
318 
319     // init instance (only one instance in this version !!!)
320     pSalData->mpInstance = pInst;
321     // this one is for outside AquaSalInstance::Yield
322     SalData::ensureThreadAutoreleasePool();
323     // no focus rects on NWF
324     ImplGetSVData()->maNWFData.mbNoFocusRects = true;
325     ImplGetSVData()->maNWFData.mbNoActiveTabTextRaise = true;
326     ImplGetSVData()->maNWFData.mbCenteredTabs = true;
327     ImplGetSVData()->maNWFData.mnStatusBarLowerRightOffset = 10;
328 
329     return pInst;
330 }
331 }
332 
AquaSalInstance()333 AquaSalInstance::AquaSalInstance()
334     : SalInstance(std::make_unique<SalYieldMutex>())
335     , mnActivePrintJobs( 0 )
336     , mbIsLiveResize( false )
337     , mbNoYieldLock( false )
338     , mbTimerProcessed( false )
339 {
340     maMainThread = osl::Thread::getCurrentIdentifier();
341 
342     ImplSVData* pSVData = ImplGetSVData();
343     pSVData->maAppData.mxToolkitName = OUString("osx");
344 }
345 
~AquaSalInstance()346 AquaSalInstance::~AquaSalInstance()
347 {
348     [NSApp stop: NSApp];
349     bLeftMain = true;
350     if( pDockMenu )
351     {
352         [pDockMenu release];
353         pDockMenu = nil;
354     }
355 }
356 
TriggerUserEventProcessing()357 void AquaSalInstance::TriggerUserEventProcessing()
358 {
359     dispatch_async(dispatch_get_main_queue(),^{
360         ImplNSAppPostEvent( AquaSalInstance::YieldWakeupEvent, NO );
361     });
362 }
363 
ProcessEvent(SalUserEvent aEvent)364 void AquaSalInstance::ProcessEvent( SalUserEvent aEvent )
365 {
366     aEvent.m_pFrame->CallCallback( aEvent.m_nEvent, aEvent.m_pData );
367     maWaitingYieldCond.set();
368 }
369 
IsMainThread() const370 bool AquaSalInstance::IsMainThread() const
371 {
372     return osl::Thread::getCurrentIdentifier() == maMainThread;
373 }
374 
handleAppDefinedEvent(NSEvent * pEvent)375 void AquaSalInstance::handleAppDefinedEvent( NSEvent* pEvent )
376 {
377     AquaSalTimer *pTimer = static_cast<AquaSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
378     int nSubtype = [pEvent subtype];
379     switch( nSubtype )
380     {
381     case AppStartTimerEvent:
382         if ( pTimer )
383             pTimer->handleStartTimerEvent( pEvent );
384         break;
385     case AppExecuteSVMain:
386     {
387         int nRet = ImplSVMain();
388         if (gpnInit)
389             *gpnInit = nRet;
390         [NSApp stop: NSApp];
391         break;
392     }
393     case DispatchTimerEvent:
394     {
395         AquaSalInstance *pInst = GetSalData()->mpInstance;
396         if ( pTimer && pInst )
397             pInst->mbTimerProcessed = pTimer->handleDispatchTimerEvent( pEvent );
398         break;
399     }
400 #if !HAVE_FEATURE_MACOSX_SANDBOX
401     case AppleRemoteControlEvent: // Defined in <apple_remote/RemoteMainController.h>
402     {
403         MediaCommand nCommand;
404         AquaSalInstance *pInst = GetSalData()->mpInstance;
405         bool bIsFullScreenMode = false;
406 
407         for( auto pSalFrame : pInst->getFrames() )
408         {
409             const AquaSalFrame* pFrame = static_cast<const AquaSalFrame*>( pSalFrame );
410             if ( pFrame->mbFullScreen )
411             {
412                 bIsFullScreenMode = true;
413                 break;
414             }
415         }
416 
417         switch ([pEvent data1])
418         {
419             case kRemoteButtonPlay:
420                 nCommand = bIsFullScreenMode ? MediaCommand::PlayPause : MediaCommand::Play;
421                 break;
422 
423             // kept for experimentation purpose (scheduled for future implementation)
424             // case kRemoteButtonMenu:         nCommand = MediaCommand::Menu; break;
425 
426             case kRemoteButtonPlus:         nCommand = MediaCommand::VolumeUp; break;
427 
428             case kRemoteButtonMinus:        nCommand = MediaCommand::VolumeDown; break;
429 
430             case kRemoteButtonRight:        nCommand = MediaCommand::NextTrack; break;
431 
432             case kRemoteButtonRight_Hold:   nCommand = MediaCommand::NextTrackHold; break;
433 
434             case kRemoteButtonLeft:         nCommand = MediaCommand::PreviousTrack; break;
435 
436             case kRemoteButtonLeft_Hold:    nCommand = MediaCommand::Rewind; break;
437 
438             case kRemoteButtonPlay_Hold:    nCommand = MediaCommand::PlayHold; break;
439 
440             case kRemoteButtonMenu_Hold:    nCommand = MediaCommand::Stop; break;
441 
442             // FIXME : not detected
443             case kRemoteButtonPlus_Hold:
444             case kRemoteButtonMinus_Hold:
445                 break;
446 
447             default:
448                 break;
449         }
450         AquaSalFrame* pFrame = static_cast<AquaSalFrame*>( pInst->anyFrame() );
451         vcl::Window* pWindow = pFrame ? pFrame->GetWindow() : nullptr;
452         if( pWindow )
453         {
454             const Point aPoint;
455             CommandMediaData aMediaData(nCommand);
456             CommandEvent aCEvt( aPoint, CommandEventId::Media, FALSE, &aMediaData );
457             NotifyEvent aNCmdEvt( MouseNotifyEvent::COMMAND, pWindow, &aCEvt );
458 
459             if ( !ImplCallPreNotify( aNCmdEvt ) )
460                 pWindow->Command( aCEvt );
461         }
462 
463     }
464     break;
465 #endif
466 
467     case YieldWakeupEvent:
468         // do nothing, fall out of Yield
469         break;
470 
471     default:
472         OSL_FAIL( "unhandled NSApplicationDefined event" );
473         break;
474     };
475 }
476 
RunInMainYield(bool bHandleAllCurrentEvents)477 bool AquaSalInstance::RunInMainYield( bool bHandleAllCurrentEvents )
478 {
479     OSX_SALDATA_RUNINMAIN_UNION( DoYield( false, bHandleAllCurrentEvents), boolean )
480     assert( false && "Don't call this from the main thread!" );
481     return false;
482 
483 }
484 
isWakeupEvent(NSEvent * pEvent)485 static bool isWakeupEvent( NSEvent *pEvent )
486 {
487 SAL_WNODEPRECATED_DECLARATIONS_PUSH
488     return NSApplicationDefined == [pEvent type]
489         && AquaSalInstance::YieldWakeupEvent == static_cast<int>([pEvent subtype]);
490 SAL_WNODEPRECATED_DECLARATIONS_POP
491 }
492 
DoYield(bool bWait,bool bHandleAllCurrentEvents)493 bool AquaSalInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
494 {
495     // ensure that the per thread autorelease pool is top level and
496     // will therefore not be destroyed by cocoa implicitly
497     SalData::ensureThreadAutoreleasePool();
498 
499     // NSAutoreleasePool documentation suggests we should have
500     // an own pool for each yield level
501     ReleasePoolHolder aReleasePool;
502 
503     // first, process current user events
504     bool bHadEvent = DispatchUserEvents( bHandleAllCurrentEvents );
505     if ( !bHandleAllCurrentEvents && bHadEvent )
506         return true;
507 
508     // handle cocoa event queue
509     // cocoa events may be only handled in the thread the NSApp was created
510     if( IsMainThread() && mnActivePrintJobs == 0 )
511     {
512         // handle available events
513         NSEvent* pEvent = nil;
514         NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
515         mbTimerProcessed = false;
516 
517         do
518         {
519             SolarMutexReleaser aReleaser;
520 
521             pEvent = [NSApp nextEventMatchingMask: NSEventMaskAny
522                             untilDate: nil
523                             inMode: NSDefaultRunLoopMode
524                             dequeue: YES];
525             if( pEvent )
526             {
527                 [NSApp sendEvent: pEvent];
528                 if ( isWakeupEvent( pEvent ) )
529                     continue;
530                 bHadEvent = true;
531             }
532 
533             [NSApp updateWindows];
534 
535             if ( !bHandleAllCurrentEvents || !pEvent || now < [pEvent timestamp] )
536                 break;
537         }
538         while( true );
539 
540         AquaSalTimer *pTimer = static_cast<AquaSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
541         if ( !mbTimerProcessed && pTimer && pTimer->IsDirectTimeout() )
542         {
543             pTimer->handleTimerElapsed();
544             bHadEvent = true;
545         }
546 
547         // if we had no event yet, wait for one if requested
548         if( bWait && ! bHadEvent )
549         {
550             SolarMutexReleaser aReleaser;
551 
552             pEvent = [NSApp nextEventMatchingMask: NSEventMaskAny
553                             untilDate: [NSDate distantFuture]
554                             inMode: NSDefaultRunLoopMode
555                             dequeue: YES];
556             if( pEvent )
557             {
558                 [NSApp sendEvent: pEvent];
559                 if ( !isWakeupEvent( pEvent ) )
560                     bHadEvent = true;
561             }
562             [NSApp updateWindows];
563         }
564 
565         // collect update rectangles
566         for( auto pSalFrame : GetSalData()->mpInstance->getFrames() )
567         {
568             AquaSalFrame* pFrame = static_cast<AquaSalFrame*>( pSalFrame );
569             if( pFrame->mbShown && ! pFrame->maInvalidRect.IsEmpty() )
570             {
571                 pFrame->Flush( pFrame->maInvalidRect );
572                 pFrame->maInvalidRect.SetEmpty();
573             }
574         }
575 
576         if ( bHadEvent )
577             maWaitingYieldCond.set();
578     }
579     else
580     {
581         bHadEvent = RunInMainYield( bHandleAllCurrentEvents );
582         if ( !bHadEvent && bWait )
583         {
584             // #i103162#
585             // wait until the main thread has dispatched an event
586             maWaitingYieldCond.reset();
587             SolarMutexReleaser aReleaser;
588             maWaitingYieldCond.wait();
589         }
590     }
591 
592     // we get some apple events way too early
593     // before the application is ready to handle them,
594     // so their corresponding application events need to be delayed
595     // now is a good time to handle at least one of them
596     if( bWait && !aAppEventList.empty() && ImplGetSVData()->maAppData.mbInAppExecute )
597     {
598         // make sure that only one application event is active at a time
599         static bool bInAppEvent = false;
600         if( !bInAppEvent )
601         {
602             bInAppEvent = true;
603             // get the next delayed application event
604             const ApplicationEvent* pAppEvent = aAppEventList.front();
605             aAppEventList.pop_front();
606             // handle one application event (no recursion)
607             const ImplSVData* pSVData = ImplGetSVData();
608             pSVData->mpApp->AppEvent( *pAppEvent );
609             delete pAppEvent;
610             // allow the next delayed application event
611             bInAppEvent = false;
612         }
613     }
614 
615     return bHadEvent;
616 }
617 
AnyInput(VclInputFlags nType)618 bool AquaSalInstance::AnyInput( VclInputFlags nType )
619 {
620     if( nType & VclInputFlags::APPEVENT )
621     {
622         if( ! aAppEventList.empty() )
623             return true;
624         if( nType == VclInputFlags::APPEVENT )
625             return false;
626     }
627 
628     OSX_INST_RUNINMAIN_UNION( AnyInput( nType ), boolean )
629 
630     if( nType & VclInputFlags::TIMER )
631     {
632         AquaSalTimer *pTimer = static_cast<AquaSalTimer*>( ImplGetSVData()->maSchedCtx.mpSalTimer );
633         if (pTimer && pTimer->IsTimerElapsed())
634             return true;
635     }
636 
637     unsigned/*NSUInteger*/ nEventMask = 0;
638 SAL_WNODEPRECATED_DECLARATIONS_PUSH
639         // 'NSFlagsChangedMask' is deprecated: first deprecated in macOS 10.12
640         // 'NSKeyDownMask' is deprecated: first deprecated in macOS 10.12
641         // 'NSKeyUpMask' is deprecated: first deprecated in macOS 10.12
642         // 'NSLeftMouseDownMask' is deprecated: first deprecated in macOS 10.12
643         // 'NSLeftMouseDraggedMask' is deprecated: first deprecated in macOS 10.12
644         // 'NSLeftMouseUpMask' is deprecated: first deprecated in macOS 10.12
645         // 'NSMouseEnteredMask' is deprecated: first deprecated in macOS 10.12
646         // 'NSMouseExitedMask' is deprecated: first deprecated in macOS 10.12
647         // 'NSOtherMouseDownMask' is deprecated: first deprecated in macOS 10.12
648         // 'NSOtherMouseDraggedMask' is deprecated: first deprecated in macOS 10.12
649         // 'NSOtherMouseUpMask' is deprecated: first deprecated in macOS 10.12
650         // 'NSRightMouseDownMask' is deprecated: first deprecated in macOS 10.12
651         // 'NSRightMouseDraggedMask' is deprecated: first deprecated in macOS 10.12
652         // 'NSRightMouseUpMask' is deprecated: first deprecated in macOS 10.12
653         // 'NSScrollWheelMask' is deprecated: first deprecated in macOS 10.12
654         // 'NSTabletPoint' is deprecated: first deprecated in macOS 10.12
655     if( nType & VclInputFlags::MOUSE)
656         nEventMask |=
657             NSLeftMouseDownMask    | NSRightMouseDownMask    | NSOtherMouseDownMask |
658             NSLeftMouseUpMask      | NSRightMouseUpMask      | NSOtherMouseUpMask |
659             NSLeftMouseDraggedMask | NSRightMouseDraggedMask | NSOtherMouseDraggedMask |
660             NSScrollWheelMask |
661             // NSMouseMovedMask |
662             NSMouseEnteredMask | NSMouseExitedMask;
663     if( nType & VclInputFlags::KEYBOARD)
664         nEventMask |= NSKeyDownMask | NSKeyUpMask | NSFlagsChangedMask;
665     if( nType & VclInputFlags::OTHER)
666         nEventMask |= NSTabletPoint | NSApplicationDefinedMask;
667 SAL_WNODEPRECATED_DECLARATIONS_POP
668     // TODO: VclInputFlags::PAINT / more VclInputFlags::OTHER
669     if( !bool(nType) )
670         return false;
671 
672     NSEvent* pEvent = [NSApp nextEventMatchingMask: nEventMask untilDate: nil
673                             inMode: NSDefaultRunLoopMode dequeue: NO];
674     return (pEvent != nullptr);
675 }
676 
CreateChildFrame(SystemParentData *,SalFrameStyleFlags)677 SalFrame* AquaSalInstance::CreateChildFrame( SystemParentData*, SalFrameStyleFlags /*nSalFrameStyle*/ )
678 {
679     return nullptr;
680 }
681 
CreateFrame(SalFrame * pParent,SalFrameStyleFlags nSalFrameStyle)682 SalFrame* AquaSalInstance::CreateFrame( SalFrame* pParent, SalFrameStyleFlags nSalFrameStyle )
683 {
684     OSX_INST_RUNINMAIN_POINTER( CreateFrame( pParent, nSalFrameStyle ), SalFrame* )
685     return new AquaSalFrame( pParent, nSalFrameStyle );
686 }
687 
DestroyFrame(SalFrame * pFrame)688 void AquaSalInstance::DestroyFrame( SalFrame* pFrame )
689 {
690     OSX_INST_RUNINMAIN( DestroyFrame( pFrame ) )
691     delete pFrame;
692 }
693 
CreateObject(SalFrame * pParent,SystemWindowData * pWindowData,bool)694 SalObject* AquaSalInstance::CreateObject( SalFrame* pParent, SystemWindowData* pWindowData, bool /* bShow */ )
695 {
696     if ( !pParent )
697         return nullptr;
698 
699     OSX_INST_RUNINMAIN_POINTER( CreateObject( pParent, pWindowData, false ), SalObject* )
700     return new AquaSalObject( static_cast<AquaSalFrame*>(pParent), pWindowData );
701 }
702 
DestroyObject(SalObject * pObject)703 void AquaSalInstance::DestroyObject( SalObject* pObject )
704 {
705     OSX_INST_RUNINMAIN( DestroyObject( pObject ) )
706     delete pObject;
707 }
708 
CreatePrinter(SalInfoPrinter * pInfoPrinter)709 std::unique_ptr<SalPrinter> AquaSalInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter )
710 {
711     return std::unique_ptr<SalPrinter>(new AquaSalPrinter( dynamic_cast<AquaSalInfoPrinter*>(pInfoPrinter) ));
712 }
713 
GetPrinterQueueInfo(ImplPrnQueueList * pList)714 void AquaSalInstance::GetPrinterQueueInfo( ImplPrnQueueList* pList )
715 {
716     NSArray* pNames = [NSPrinter printerNames];
717     NSArray* pTypes = [NSPrinter printerTypes];
718     unsigned int nNameCount = pNames ? [pNames count] : 0;
719     unsigned int nTypeCount = pTypes ? [pTypes count] : 0;
720     SAL_WARN_IF( nTypeCount != nNameCount, "vcl", "type count not equal to printer count" );
721     for( unsigned int i = 0; i < nNameCount; i++ )
722     {
723         NSString* pName = [pNames objectAtIndex: i];
724         NSString* pType = i < nTypeCount ? [pTypes objectAtIndex: i] : nil;
725         if( pName )
726         {
727             std::unique_ptr<SalPrinterQueueInfo> pInfo(new SalPrinterQueueInfo);
728             pInfo->maPrinterName    = GetOUString( pName );
729             if( pType )
730                 pInfo->maDriver     = GetOUString( pType );
731             pInfo->mnStatus         = PrintQueueFlags::NONE;
732             pInfo->mnJobs           = 0;
733 
734             pList->Add( std::move(pInfo) );
735         }
736     }
737 }
738 
GetPrinterQueueState(SalPrinterQueueInfo *)739 void AquaSalInstance::GetPrinterQueueState( SalPrinterQueueInfo* )
740 {
741 }
742 
GetDefaultPrinter()743 OUString AquaSalInstance::GetDefaultPrinter()
744 {
745     // #i113170# may not be the main thread if called from UNO API
746     SalData::ensureThreadAutoreleasePool();
747 
748     if( maDefaultPrinter.isEmpty() )
749     {
750         NSPrintInfo* pPI = [NSPrintInfo sharedPrintInfo];
751         SAL_WARN_IF( !pPI, "vcl", "no print info" );
752         if( pPI )
753         {
754             NSPrinter* pPr = [pPI printer];
755             SAL_WARN_IF( !pPr, "vcl", "no printer in default info" );
756             if( pPr )
757             {
758                 NSString* pDefName = [pPr name];
759                 SAL_WARN_IF( !pDefName, "vcl", "printer has no name" );
760                 maDefaultPrinter = GetOUString( pDefName );
761             }
762         }
763     }
764     return maDefaultPrinter;
765 }
766 
CreateInfoPrinter(SalPrinterQueueInfo * pQueueInfo,ImplJobSetup * pSetupData)767 SalInfoPrinter* AquaSalInstance::CreateInfoPrinter( SalPrinterQueueInfo* pQueueInfo,
768                                                     ImplJobSetup* pSetupData )
769 {
770     // #i113170# may not be the main thread if called from UNO API
771     SalData::ensureThreadAutoreleasePool();
772 
773     SalInfoPrinter* pNewInfoPrinter = nullptr;
774     if( pQueueInfo )
775     {
776         pNewInfoPrinter = new AquaSalInfoPrinter( *pQueueInfo );
777         if( pSetupData )
778             pNewInfoPrinter->SetPrinterData( pSetupData );
779     }
780 
781     return pNewInfoPrinter;
782 }
783 
DestroyInfoPrinter(SalInfoPrinter * pPrinter)784 void AquaSalInstance::DestroyInfoPrinter( SalInfoPrinter* pPrinter )
785 {
786     // #i113170# may not be the main thread if called from UNO API
787     SalData::ensureThreadAutoreleasePool();
788 
789     delete pPrinter;
790 }
791 
GetConnectionIdentifier()792 OUString AquaSalInstance::GetConnectionIdentifier()
793 {
794     return OUString();
795 }
796 
797 // We need to re-encode file urls because osl_getFileURLFromSystemPath converts
798 // to UTF-8 before encoding non ascii characters, which is not what other apps expect.
translateToExternalUrl(const OUString & internalUrl)799 static OUString translateToExternalUrl(const OUString& internalUrl)
800 {
801     uno::Reference< uno::XComponentContext > context(
802         comphelper::getProcessComponentContext());
803     return uri::ExternalUriReferenceTranslator::create(context)->translateToExternal(internalUrl);
804 }
805 
806 // #i104525# many versions of OSX have problems with some URLs:
807 // when an app requests OSX to add one of these URLs to the "Recent Items" list
808 // then this app gets killed (TextEdit, Preview, etc. and also OOo)
isDangerousUrl(const OUString & rUrl)809 static bool isDangerousUrl( const OUString& rUrl )
810 {
811     // use a heuristic that detects all known cases since there is no official comment
812     // on the exact impact and root cause of the OSX bug
813     const int nLen = rUrl.getLength();
814     const sal_Unicode* p = rUrl.getStr();
815     for( int i = 0; i < nLen-3; ++i, ++p ) {
816         if( p[0] != '%' )
817             continue;
818         // escaped percent?
819         if( (p[1] == '2') && (p[2] == '5') )
820             return true;
821         // escapes are considered to be UTF-8 encoded
822         // => check for invalid UTF-8 leading byte
823         if( (p[1] != 'f') && (p[1] != 'F') )
824             continue;
825         int cLowNibble = p[2];
826         if( (cLowNibble >= '0' ) && (cLowNibble <= '9'))
827             return false;
828         if( cLowNibble >= 'a' )
829             cLowNibble -= 'a' - 'A';
830         if( (cLowNibble < 'A') || (cLowNibble >= 'C'))
831             return true;
832     }
833 
834     return false;
835 }
836 
AddToRecentDocumentList(const OUString & rFileUrl,const OUString &,const OUString &)837 void AquaSalInstance::AddToRecentDocumentList(const OUString& rFileUrl, const OUString& /*rMimeType*/, const OUString& /*rDocumentService*/)
838 {
839     // Convert file URL for external use (see above)
840     OUString externalUrl = translateToExternalUrl(rFileUrl);
841     if( externalUrl.isEmpty() )
842         externalUrl = rFileUrl;
843 
844     if( !externalUrl.isEmpty() && !isDangerousUrl( externalUrl ) )
845     {
846         NSString* pString = CreateNSString( externalUrl );
847         NSURL* pURL = [NSURL URLWithString: pString];
848 
849         if( pURL )
850         {
851             NSDocumentController* pCtrl = [NSDocumentController sharedDocumentController];
852             [pCtrl noteNewRecentDocumentURL: pURL];
853         }
854         if( pString )
855             [pString release];
856     }
857 }
858 
CreateSalTimer()859 SalTimer* AquaSalInstance::CreateSalTimer()
860 {
861     return new AquaSalTimer();
862 }
863 
CreateSalSystem()864 SalSystem* AquaSalInstance::CreateSalSystem()
865 {
866     return new AquaSalSystem();
867 }
868 
CreateSalBitmap()869 std::shared_ptr<SalBitmap> AquaSalInstance::CreateSalBitmap()
870 {
871     return std::make_shared<QuartzSalBitmap>();
872 }
873 
getOSVersion()874 OUString AquaSalInstance::getOSVersion()
875 {
876     NSString * versionString = nullptr;
877     NSDictionary * sysVersionDict = [ NSDictionary dictionaryWithContentsOfFile: @"/System/Library/CoreServices/SystemVersion.plist" ];
878     if ( sysVersionDict )
879         versionString = [ sysVersionDict valueForKey: @"ProductVersion" ];
880 
881     OUString aVersion = "Mac OS X ";
882     if ( versionString )
883         aVersion += OUString::fromUtf8( [ versionString UTF8String ] );
884     else
885         aVersion += "(unknown)";
886 
887     return aVersion;
888 }
889 
CreateCGImage(const Image & rImage)890 CGImageRef CreateCGImage( const Image& rImage )
891 {
892     BitmapEx aBmpEx( rImage.GetBitmapEx() );
893     Bitmap aBmp( aBmpEx.GetBitmap() );
894 
895     if( ! aBmp || ! aBmp.ImplGetSalBitmap() )
896         return nullptr;
897 
898     // simple case, no transparency
899     QuartzSalBitmap* pSalBmp = static_cast<QuartzSalBitmap*>(aBmp.ImplGetSalBitmap().get());
900 
901     if( ! pSalBmp )
902         return nullptr;
903 
904     CGImageRef xImage = nullptr;
905     if( ! (aBmpEx.IsAlpha() || aBmpEx.IsTransparent() ) )
906         xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
907     else if( aBmpEx.IsAlpha() )
908     {
909         AlphaMask aAlphaMask( aBmpEx.GetAlpha() );
910         Bitmap aMask( aAlphaMask.GetBitmap() );
911         QuartzSalBitmap* pMaskBmp = static_cast<QuartzSalBitmap*>(aMask.ImplGetSalBitmap().get());
912         if( pMaskBmp )
913             xImage = pSalBmp->CreateWithMask( *pMaskBmp, 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
914         else
915             xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
916     }
917     else if( aBmpEx.GetTransparentType() == TransparentType::Bitmap )
918     {
919         Bitmap aMask( aBmpEx.GetMask() );
920         QuartzSalBitmap* pMaskBmp = static_cast<QuartzSalBitmap*>(aMask.ImplGetSalBitmap().get());
921         if( pMaskBmp )
922             xImage = pSalBmp->CreateWithMask( *pMaskBmp, 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
923         else
924             xImage = pSalBmp->CreateCroppedImage( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight );
925     }
926     else if( aBmpEx.GetTransparentType() == TransparentType::Color )
927     {
928         Color aTransColor( aBmpEx.GetTransparentColor() );
929         Color nTransColor( aTransColor.GetRed(), aTransColor.GetGreen(), aTransColor.GetBlue() );
930         xImage = pSalBmp->CreateColorMask( 0, 0, pSalBmp->mnWidth, pSalBmp->mnHeight, nTransColor );
931     }
932 
933     return xImage;
934 }
935 
CreateNSImage(const Image & rImage)936 NSImage* CreateNSImage( const Image& rImage )
937 {
938     CGImageRef xImage = CreateCGImage( rImage );
939 
940     if( ! xImage )
941         return nil;
942 
943     Size aSize( rImage.GetSizePixel() );
944     NSImage* pImage = [[NSImage alloc] initWithSize: NSMakeSize( aSize.Width(), aSize.Height() )];
945     if( pImage )
946     {
947         [pImage lockFocusFlipped:YES];
948         NSGraphicsContext* pContext = [NSGraphicsContext currentContext];
949         CGContextRef rCGContext = [pContext CGContext];
950 
951         const CGRect aDstRect = { {0, 0}, { static_cast<CGFloat>(aSize.Width()), static_cast<CGFloat>(aSize.Height()) } };
952         CGContextDrawImage( rCGContext, aDstRect, xImage );
953 
954         [pImage unlockFocus];
955     }
956 
957     CGImageRelease( xImage );
958 
959     return pImage;
960 }
961 
SVMainHook(int * pnInit)962 bool AquaSalInstance::SVMainHook(int* pnInit)
963 {
964     gpnInit = pnInit;
965 
966     OUString aExeURL, aExe;
967     osl_getExecutableFile( &aExeURL.pData );
968     osl_getSystemPathFromFileURL( aExeURL.pData, &aExe.pData );
969     OString aByteExe( OUStringToOString( aExe, osl_getThreadTextEncoding() ) );
970 
971 #ifdef DEBUG
972     aByteExe += OString ( " NSAccessibilityDebugLogLevel 1" );
973     const char* pArgv[] = { aByteExe.getStr(), NULL };
974     NSApplicationMain( 3, pArgv );
975 #else
976     const char* pArgv[] = { aByteExe.getStr(), nullptr };
977     NSApplicationMain( 1, pArgv );
978 #endif
979 
980     return true;
981 }
982 
983 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
984