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