1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
22 #include <osl/diagnose.h>
24 #include <condition_variable>
28 #include <config_features.h>
32 #include <comphelper/solarmutex.hxx>
34 #include <comphelper/lok.hxx>
36 #include <osl/process.h>
38 #include <rtl/ustrbuf.hxx>
39 #include <vclpluginapi.h>
40 #include <vcl/QueueInfo.hxx>
41 #include <vcl/svapp.hxx>
42 #include <vcl/window.hxx>
43 #include <vcl/idle.hxx>
44 #include <vcl/svmain.hxx>
45 #include <vcl/opengl/OpenGLContext.hxx>
46 #include <vcl/commandevent.hxx>
47 #include <vcl/event.hxx>
49 #include <osx/saldata.hxx>
50 #include <osx/salinst.h>
51 #include <osx/salframe.h>
52 #include <osx/salobj.h>
53 #include <osx/salsys.h>
54 #include <quartz/salvd.h>
55 #include <quartz/salbmp.h>
56 #include <quartz/utils.h>
57 #include <osx/salprn.h>
58 #include <osx/saltimer.h>
59 #include <osx/vclnsapp.h>
60 #include <osx/runinmain.hxx>
63 #include <strings.hrc>
65 #include <comphelper/processfactory.hxx>
67 #include <com/sun/star/uri/ExternalUriReferenceTranslator.hpp>
68 #include <com/sun/star/uno/XComponentContext.hpp>
71 #include <Foundation/Foundation.h>
72 #include <ApplicationServices/ApplicationServices.h>
73 #import "apple_remote/RemoteMainController.h"
74 #include <apple_remote/RemoteControl.h>
78 #include <vcl/skia/SkiaHelper.hxx>
79 #include <skia/salbmp.hxx>
80 #include <skia/osx/gdiimpl.hxx>
81 #include <skia/osx/bitmap.hxx>
85 #include <crt_externs.h>
88 using namespace ::com::sun::star
;
90 static int* gpnInit
= nullptr;
91 static NSMenu
* pDockMenu
= nil
;
92 static bool bLeftMain
= false;
96 class AquaDelayedSettingsChanged
: public Idle
101 AquaDelayedSettingsChanged( bool bInvalidate
) :
102 Idle("AquaSalInstance AquaDelayedSettingsChanged"),
103 mbInvalidate( bInvalidate
)
107 virtual void Invoke() override
109 AquaSalInstance
*pInst
= GetSalData()->mpInstance
;
110 SalFrame
*pAnyFrame
= pInst
->anyFrame();
112 pAnyFrame
->CallCallback( SalEvent::SettingsChanged
, nullptr );
116 for( auto pSalFrame
: pInst
->getFrames() )
118 AquaSalFrame
* pFrame
= static_cast<AquaSalFrame
*>( pSalFrame
);
119 if( pFrame
->mbShown
)
120 pFrame
->SendPaintEvent();
129 static OUString
& getFallbackPrinterName()
131 static OUString aFallbackPrinter
;
133 if ( aFallbackPrinter
.isEmpty() )
135 aFallbackPrinter
= VclResId( SV_PRINT_DEFPRT_TXT
);
136 if ( aFallbackPrinter
.isEmpty() )
137 aFallbackPrinter
= "Printer";
140 return aFallbackPrinter
;
143 void AquaSalInstance::delayedSettingsChanged( bool bInvalidate
)
145 osl::Guard
< comphelper::SolarMutex
> aGuard( *GetYieldMutex() );
146 AquaDelayedSettingsChanged
* pIdle
= new AquaDelayedSettingsChanged( bInvalidate
);
150 // the std::list<const ApplicationEvent*> must be available before any SalData/SalInst/etc. objects are ready
151 std::list
<const ApplicationEvent
*> AquaSalInstance::aAppEventList
;
153 NSMenu
* AquaSalInstance::GetDynamicDockMenu()
155 if( ! pDockMenu
&& ! bLeftMain
)
156 pDockMenu
= [[NSMenu alloc
] initWithTitle
: @
""];
160 bool AquaSalInstance::isOnCommandLine( const OUString
& rArg
)
162 sal_uInt32 nArgs
= osl_getCommandArgCount();
163 for( sal_uInt32 i
= 0; i
< nArgs
; i
++ )
166 osl_getCommandArg( i
, &aArg
.pData
);
167 if( aArg
.equals( rArg
) )
173 void AquaSalInstance::AfterAppInit()
175 [[NSNotificationCenter defaultCenter
] addObserver
: NSApp
176 selector
: @
selector(systemColorsChanged
:)
177 name
: NSSystemColorsDidChangeNotification
179 [[NSNotificationCenter defaultCenter
] addObserver
: NSApp
180 selector
: @
selector(screenParametersChanged
:)
181 name
: NSApplicationDidChangeScreenParametersNotification
183 // add observers for some settings changes that affect vcl's settings
185 [[NSDistributedNotificationCenter defaultCenter
] addObserver
: NSApp
186 selector
: @
selector(scrollbarVariantChanged
:)
187 name
: @
"AppleAquaScrollBarVariantChanged"
189 // scrollbar page behavior ("jump to here" or not)
190 [[NSDistributedNotificationCenter defaultCenter
] addObserver
: NSApp
191 selector
: @
selector(scrollbarSettingsChanged
:)
192 name
: @
"AppleNoRedisplayAppearancePreferenceChanged"
194 #if !HAVE_FEATURE_MACOSX_SANDBOX
195 // Initialize Apple Remote
196 GetSalData()->mpAppleRemoteMainController
= [[AppleRemoteMainController alloc
] init
];
198 [[NSDistributedNotificationCenter defaultCenter
] addObserver
: NSApp
199 selector
: @
selector(applicationWillBecomeActive
:)
200 name
: @
"AppleRemoteWillBecomeActive"
203 [[NSDistributedNotificationCenter defaultCenter
] addObserver
: NSApp
204 selector
: @
selector(applicationWillResignActive
:)
205 name
: @
"AppleRemoteWillResignActive"
209 // HACK: When the first call to [NSSpellChecker sharedSpellChecker] (in
210 // lingucomponent/source/spellcheck/macosxspell/macspellimp.mm) is done both on a thread other
211 // than the main thread and with the SolarMutex erroneously locked, then that can lead to
212 // deadlock as [NSSpellChecker sharedSpellChecker] internally calls
213 // AppKit`-[NSSpellChecker init] ->
214 // AppKit`-[NSSpellChecker _fillSpellCheckerPopupButton:] ->
215 // AppKit`-[NSApplication(NSServicesMenuPrivate) _fillSpellCheckerPopupButton:] ->
216 // AppKit`-[NSMenu insertItem:atIndex:] ->
217 // Foundation`-[NSNotificationCenter postNotificationName:object:userInfo:] ->
218 // CoreFoundation`_CFXNotificationPost ->
219 // Foundation`-[NSOperation waitUntilFinished]
220 // waiting for work to be done on the main thread, but the main thread is typically already
221 // blocked (in some event handling loop) waiting to acquire the SolarMutex. The real solution
222 // would be to fix all the cases where a call to [NSSpellChecker sharedSpellChecker] in
223 // lingucomponent/source/spellcheck/macosxspell/macspellimp.mm is done while the SolarMutex is
224 // locked (somewhere up the call chain), but that appears to be rather difficult (see e.g.
225 // <https://bugs.documentfoundation.org/show_bug.cgi?id=151894> "FILEOPEN a Base Document with
226 // customized event for open a startform by 'open document' LO stuck"). So, at least for now,
227 // chicken out and do that first call to [NSSpellChecker sharedSpellChecker] upfront in a
228 // controlled environment:
229 [NSSpellChecker sharedSpellChecker
];
232 SalYieldMutex::SalYieldMutex()
233 : m_aCodeBlock( nullptr )
237 SalYieldMutex::~SalYieldMutex()
241 void SalYieldMutex::doAcquire( sal_uInt32 nLockCount
)
243 AquaSalInstance
*pInst
= GetSalData()->mpInstance
;
244 if ( pInst
&& pInst
->IsMainThread() )
246 if ( pInst
->mbNoYieldLock
)
249 RuninmainBlock block
= nullptr;
251 std::unique_lock
<std::mutex
> g(m_runInMainMutex
);
252 if (m_aMutex
.tryToAcquire()) {
253 assert(m_aCodeBlock
== nullptr);
254 m_wakeUpMain
= false;
257 // wait for doRelease() or RUNINMAIN_* to set the condition
258 m_aInMainCondition
.wait(g
, [this]() { return m_wakeUpMain
; });
259 m_wakeUpMain
= false;
260 std::swap(block
, m_aCodeBlock
);
264 assert( !pInst
->mbNoYieldLock
);
265 pInst
->mbNoYieldLock
= true;
267 pInst
->mbNoYieldLock
= false;
268 Block_release( block
);
269 std::scoped_lock
<std::mutex
> g(m_runInMainMutex
);
270 assert(!m_resultReady
);
271 m_resultReady
= true;
272 m_aResultCondition
.notify_all();
282 comphelper::SolarMutex::doAcquire( nLockCount
);
285 sal_uInt32
SalYieldMutex::doRelease( const bool bUnlockAll
)
287 AquaSalInstance
*pInst
= GetSalData()->mpInstance
;
288 if ( pInst
->mbNoYieldLock
&& pInst
->IsMainThread() )
292 std::scoped_lock
<std::mutex
> g(m_runInMainMutex
);
293 // read m_nCount before doRelease
294 bool const isReleased(bUnlockAll
|| m_nCount
== 1);
295 nCount
= comphelper::SolarMutex::doRelease( bUnlockAll
);
296 if (isReleased
&& !pInst
->IsMainThread()) {
298 m_aInMainCondition
.notify_all();
304 bool SalYieldMutex::IsCurrentThread() const
306 if ( !GetSalData()->mpInstance
->mbNoYieldLock
)
307 return comphelper::SolarMutex::IsCurrentThread();
309 return GetSalData()->mpInstance
->IsMainThread();
312 // some convenience functions regarding the yield mutex, aka solar mutex
314 bool ImplSalYieldMutexTryToAcquire()
316 AquaSalInstance
* pInst
= GetSalData()->mpInstance
;
318 return pInst
->GetYieldMutex()->tryToAcquire();
323 void ImplSalYieldMutexRelease()
325 AquaSalInstance
* pInst
= GetSalData()->mpInstance
;
327 pInst
->GetYieldMutex()->release();
331 VCLPLUG_OSX_PUBLIC SalInstance
* create_SalInstance()
333 SalData
* pSalData
= new SalData
;
335 NSAutoreleasePool
* pool
= [ [ NSAutoreleasePool alloc
] init
];
336 unlink([[NSString stringWithFormat
:@
"%@/Library/Saved Application State/%s.savedState/restorecount.plist", NSHomeDirectory(), MACOSX_BUNDLE_IDENTIFIER
] UTF8String
]);
337 unlink([[NSString stringWithFormat
:@
"%@/Library/Saved Application State/%s.savedState/restorecount.txt", NSHomeDirectory(), MACOSX_BUNDLE_IDENTIFIER
] UTF8String
]);
340 // create our cocoa NSApplication
341 [VCL_NSApplication sharedApplication
];
343 SalData::ensureThreadAutoreleasePool();
345 // put cocoa into multithreaded mode
346 [NSThread detachNewThreadSelector
:@
selector(enableCocoaThreads
:) toTarget
:[[CocoaThreadEnabler alloc
] init
] withObject
:nil
];
348 // activate our delegate methods
349 [NSApp setDelegate
: NSApp
];
351 SAL_WARN_IF( pSalData
->mpInstance
!= nullptr, "vcl", "more than one instance created" );
352 AquaSalInstance
* pInst
= new AquaSalInstance
;
354 // init instance (only one instance in this version !!!)
355 pSalData
->mpInstance
= pInst
;
356 // this one is for outside AquaSalInstance::Yield
357 SalData::ensureThreadAutoreleasePool();
358 // no focus rects on NWF
359 ImplGetSVData()->maNWFData
.mbNoFocusRects
= true;
360 ImplGetSVData()->maNWFData
.mbNoActiveTabTextRaise
= true;
361 ImplGetSVData()->maNWFData
.mbCenteredTabs
= true;
362 ImplGetSVData()->maNWFData
.mnStatusBarLowerRightOffset
= 10;
368 AquaSalInstance::AquaSalInstance()
369 : SalInstance(std::make_unique
<SalYieldMutex
>())
370 , mnActivePrintJobs( 0 )
371 , mbNoYieldLock( false )
372 , mbTimerProcessed( false )
374 maMainThread
= osl::Thread::getCurrentIdentifier();
376 ImplSVData
* pSVData
= ImplGetSVData();
377 pSVData
->maAppData
.mxToolkitName
= OUString("osx");
378 m_bSupportsOpenGL
= true;
380 mpButtonCell
= [[NSButtonCell alloc
] init
];
381 mpCheckCell
= [[NSButtonCell alloc
] init
];
382 mpRadioCell
= [[NSButtonCell alloc
] init
];
383 mpTextFieldCell
= [[NSTextFieldCell alloc
] initTextCell
:@
""];
384 mpComboBoxCell
= [[NSComboBoxCell alloc
] initTextCell
:@
""];
385 mpPopUpButtonCell
= [[NSPopUpButtonCell alloc
] init
];
386 mpStepperCell
= [[NSStepperCell alloc
] init
];
387 mpListNodeCell
= [[NSButtonCell alloc
] init
];
389 #if HAVE_FEATURE_SKIA
390 AquaSkiaSalGraphicsImpl::prepareSkia();
394 AquaSalInstance::~AquaSalInstance()
404 [mpListNodeCell release
];
405 [mpStepperCell release
];
406 [mpPopUpButtonCell release
];
407 [mpComboBoxCell release
];
408 [mpTextFieldCell release
];
409 [mpRadioCell release
];
410 [mpCheckCell release
];
411 [mpButtonCell release
];
413 #if HAVE_FEATURE_SKIA
414 SkiaHelper::cleanup();
418 void AquaSalInstance::TriggerUserEventProcessing()
420 dispatch_async(dispatch_get_main_queue(),^{
421 ImplNSAppPostEvent( AquaSalInstance::YieldWakeupEvent
, NO
);
425 void AquaSalInstance::ProcessEvent( SalUserEvent aEvent
)
427 aEvent
.m_pFrame
->CallCallback( aEvent
.m_nEvent
, aEvent
.m_pData
);
428 maWaitingYieldCond
.set();
431 bool AquaSalInstance::IsMainThread() const
433 return osl::Thread::getCurrentIdentifier() == maMainThread
;
436 void AquaSalInstance::handleAppDefinedEvent( NSEvent
* pEvent
)
438 AquaSalTimer
*pTimer
= static_cast<AquaSalTimer
*>( ImplGetSVData()->maSchedCtx
.mpSalTimer
);
439 int nSubtype
= [pEvent subtype
];
442 case AppStartTimerEvent
:
444 pTimer
->handleStartTimerEvent( pEvent
);
446 case AppExecuteSVMain
:
448 int nRet
= ImplSVMain();
454 case DispatchTimerEvent
:
456 AquaSalInstance
*pInst
= GetSalData()->mpInstance
;
457 if ( pTimer
&& pInst
)
458 pInst
->mbTimerProcessed
= pTimer
->handleDispatchTimerEvent( pEvent
);
461 #if !HAVE_FEATURE_MACOSX_SANDBOX
462 case AppleRemoteControlEvent
: // Defined in <apple_remote/RemoteMainController.h>
464 MediaCommand nCommand
;
465 AquaSalInstance
*pInst
= GetSalData()->mpInstance
;
466 bool bIsFullScreenMode
= false;
468 for( auto pSalFrame
: pInst
->getFrames() )
470 const AquaSalFrame
* pFrame
= static_cast<const AquaSalFrame
*>( pSalFrame
);
471 if ( pFrame
->mbFullScreen
)
473 bIsFullScreenMode
= true;
478 switch ([pEvent data1
])
480 case kRemoteButtonPlay
:
481 nCommand
= bIsFullScreenMode
? MediaCommand::PlayPause
: MediaCommand::Play
;
484 // kept for experimentation purpose (scheduled for future implementation)
485 // case kRemoteButtonMenu: nCommand = MediaCommand::Menu; break;
487 case kRemoteButtonPlus
: nCommand
= MediaCommand::VolumeUp
; break;
489 case kRemoteButtonMinus
: nCommand
= MediaCommand::VolumeDown
; break;
491 case kRemoteButtonRight
: nCommand
= MediaCommand::NextTrack
; break;
493 case kRemoteButtonRight_Hold
: nCommand
= MediaCommand::NextTrackHold
; break;
495 case kRemoteButtonLeft
: nCommand
= MediaCommand::PreviousTrack
; break;
497 case kRemoteButtonLeft_Hold
: nCommand
= MediaCommand::Rewind
; break;
499 case kRemoteButtonPlay_Hold
: nCommand
= MediaCommand::PlayHold
; break;
501 case kRemoteButtonMenu_Hold
: nCommand
= MediaCommand::Stop
; break;
503 // FIXME : not detected
504 case kRemoteButtonPlus_Hold
:
505 case kRemoteButtonMinus_Hold
:
511 AquaSalFrame
* pFrame
= static_cast<AquaSalFrame
*>( pInst
->anyFrame() );
512 vcl::Window
* pWindow
= pFrame
? pFrame
->GetWindow() : nullptr;
516 CommandMediaData
aMediaData(nCommand
);
517 CommandEvent
aCEvt( aPoint
, CommandEventId::Media
, false, &aMediaData
);
518 NotifyEvent
aNCmdEvt( NotifyEventType::COMMAND
, pWindow
, &aCEvt
);
520 if ( !ImplCallPreNotify( aNCmdEvt
) )
521 pWindow
->Command( aCEvt
);
528 case YieldWakeupEvent
:
529 // do nothing, fall out of Yield
533 OSL_FAIL( "unhandled NSEventTypeApplicationDefined event" );
538 bool AquaSalInstance::RunInMainYield( bool bHandleAllCurrentEvents
)
540 OSX_SALDATA_RUNINMAIN_UNION( DoYield( false, bHandleAllCurrentEvents
), boolean
)
542 // PrinterController::removeTransparencies() calls this frequently on the
543 // main thread so reduce the severity from an assert so that printing still
544 // works in a debug builds
545 SAL_WARN_IF( true, "vcl", "Don't call this from the main thread!" );
550 static bool isWakeupEvent( NSEvent
*pEvent
)
552 return NSEventTypeApplicationDefined
== [pEvent type
]
553 && AquaSalInstance::YieldWakeupEvent
== static_cast<int>([pEvent subtype
]);
556 bool AquaSalInstance::DoYield(bool bWait
, bool bHandleAllCurrentEvents
)
558 // ensure that the per thread autorelease pool is top level and
559 // will therefore not be destroyed by cocoa implicitly
560 SalData::ensureThreadAutoreleasePool();
562 // NSAutoreleasePool documentation suggests we should have
563 // an own pool for each yield level
564 ReleasePoolHolder aReleasePool
;
566 // first, process current user events
567 // Related: tdf#152703 Eliminate potential blocking during live resize
568 // Only native events and timers need to be dispatched to redraw
569 // the window so skip dispatching user events when a window is in
571 bool bHadEvent
= ( !ImplGetSVData()->mpWinData
->mbIsLiveResize
&& DispatchUserEvents( bHandleAllCurrentEvents
) );
572 if ( !bHandleAllCurrentEvents
&& bHadEvent
)
575 // handle cocoa event queue
576 // cocoa events may be only handled in the thread the NSApp was created
577 if( IsMainThread() && mnActivePrintJobs
== 0 )
579 // handle available events
580 NSEvent
* pEvent
= nil
;
581 NSTimeInterval now
= [[NSProcessInfo processInfo
] systemUptime
];
582 mbTimerProcessed
= false;
587 SolarMutexReleaser aReleaser
;
589 pEvent
= [NSApp nextEventMatchingMask
: NSEventMaskAny
590 untilDate
: [NSDate distantPast
]
591 inMode
: NSDefaultRunLoopMode
595 // tdf#155092 don't dispatch left mouse up events during live resizing
596 // If this is a left mouse up event, dispatching this event
597 // will trigger tdf#155092 to occur in the next mouse down
598 // event. So do not dispatch this event and push it back onto
599 // the front of the event queue so no more events will be
600 // dispatched until live resizing ends. Surprisingly, live
601 // resizing appears to end in the next mouse down event.
602 if ( ImplGetSVData()->mpWinData
->mbIsLiveResize
&& [pEvent type
] == NSEventTypeLeftMouseUp
)
604 [NSApp postEvent
: pEvent atStart
: YES
];
608 [NSApp sendEvent
: pEvent
];
609 if ( isWakeupEvent( pEvent
) )
614 [NSApp updateWindows
];
616 if ( !bHandleAllCurrentEvents
|| !pEvent
|| now
< [pEvent timestamp
] )
618 // noelgrandin: I see sporadic hangs on the macos jenkins boxes, and the backtrace
619 // points to the this loop - let us see if breaking out of here after too many
620 // trips around helps.
627 AquaSalTimer
*pTimer
= static_cast<AquaSalTimer
*>( ImplGetSVData()->maSchedCtx
.mpSalTimer
);
628 if ( !mbTimerProcessed
&& pTimer
&& pTimer
->IsDirectTimeout() )
630 pTimer
->handleTimerElapsed();
634 // if we had no event yet, wait for one if requested
635 // Related: tdf#152703 Eliminate potential blocking during live resize
636 // Some events and timers call Application::Reschedule() or
637 // Application::Yield() so don't block and wait for events when a
638 // window is in live resize
639 if( bWait
&& ! bHadEvent
&& !ImplGetSVData()->mpWinData
->mbIsLiveResize
)
641 SolarMutexReleaser aReleaser
;
643 // attempt to fix macos jenkins hangs - part 3
644 // oox::xls::WorkbookFragment::finalizeImport() calls
645 // AquaSalInstance::DoYield() with bWait set to true. But
646 // since unit tests generally have no expected user generated
647 // events, we can end up blocking and waiting forever so
648 // don't block and wait when running unit tests.
649 pEvent
= [NSApp nextEventMatchingMask
: NSEventMaskAny
650 untilDate
: SalInstance::IsRunningUnitTest() ? [NSDate distantPast
] : [NSDate distantFuture
]
651 inMode
: NSDefaultRunLoopMode
655 [NSApp sendEvent
: pEvent
];
656 if ( !isWakeupEvent( pEvent
) )
659 [NSApp updateWindows
];
662 // collect update rectangles
663 for( auto pSalFrame
: GetSalData()->mpInstance
->getFrames() )
665 AquaSalFrame
* pFrame
= static_cast<AquaSalFrame
*>( pSalFrame
);
666 if( pFrame
->mbShown
&& ! pFrame
->maInvalidRect
.IsEmpty() )
668 pFrame
->Flush( pFrame
->maInvalidRect
);
669 pFrame
->maInvalidRect
.SetEmpty();
674 maWaitingYieldCond
.set();
678 bHadEvent
= RunInMainYield( bHandleAllCurrentEvents
);
679 if ( !bHadEvent
&& bWait
)
682 // wait until the main thread has dispatched an event
683 maWaitingYieldCond
.reset();
684 SolarMutexReleaser aReleaser
;
685 maWaitingYieldCond
.wait();
689 // we get some apple events way too early
690 // before the application is ready to handle them,
691 // so their corresponding application events need to be delayed
692 // now is a good time to handle at least one of them
693 if( bWait
&& !aAppEventList
.empty() && ImplGetSVData()->maAppData
.mbInAppExecute
)
695 // make sure that only one application event is active at a time
696 static bool bInAppEvent
= false;
700 // get the next delayed application event
701 const ApplicationEvent
* pAppEvent
= aAppEventList
.front();
702 aAppEventList
.pop_front();
703 // handle one application event (no recursion)
704 const ImplSVData
* pSVData
= ImplGetSVData();
705 pSVData
->mpApp
->AppEvent( *pAppEvent
);
707 // allow the next delayed application event
715 bool AquaSalInstance::AnyInput( VclInputFlags nType
)
717 if( nType
& VclInputFlags::APPEVENT
)
719 if( ! aAppEventList
.empty() )
721 if( nType
== VclInputFlags::APPEVENT
)
725 OSX_INST_RUNINMAIN_UNION( AnyInput( nType
), boolean
)
727 if( nType
& VclInputFlags::TIMER
)
729 AquaSalTimer
*pTimer
= static_cast<AquaSalTimer
*>( ImplGetSVData()->maSchedCtx
.mpSalTimer
);
730 if (pTimer
&& pTimer
->IsTimerElapsed())
734 unsigned/*NSUInteger*/ nEventMask
= 0;
735 if( nType
& VclInputFlags::MOUSE
)
738 NSEventMaskLeftMouseDown
| NSEventMaskRightMouseDown
| NSEventMaskOtherMouseDown
|
739 NSEventMaskLeftMouseUp
| NSEventMaskRightMouseUp
| NSEventMaskOtherMouseUp
|
740 NSEventMaskLeftMouseDragged
| NSEventMaskRightMouseDragged
| NSEventMaskOtherMouseDragged
|
741 NSEventMaskScrollWheel
|
742 // NSEventMaskMouseMoved |
743 NSEventMaskMouseEntered
| NSEventMaskMouseExited
;
745 // Related: tdf#155266 stop delaying painting timer while swiping
746 // After fixing several flushing issues in tdf#155266, scrollbars
747 // still will not redraw until swiping has ended or paused when
748 // using Skia/Raster or Skia disabled. So, stop the delay by only
749 // including NSEventMaskScrollWheel if the current event type is
750 // not NSEventTypeScrollWheel.
751 NSEvent
* pCurrentEvent
= [NSApp currentEvent
];
752 if( pCurrentEvent
&& [pCurrentEvent type
] == NSEventTypeScrollWheel
)
753 nEventMask
&= ~NSEventMaskScrollWheel
;
756 if( nType
& VclInputFlags::KEYBOARD
)
757 nEventMask
|= NSEventMaskKeyDown
| NSEventMaskKeyUp
| NSEventMaskFlagsChanged
;
758 if( nType
& VclInputFlags::OTHER
)
759 nEventMask
|= NSEventMaskTabletPoint
| NSEventMaskApplicationDefined
;
760 // TODO: VclInputFlags::PAINT / more VclInputFlags::OTHER
764 NSEvent
* pEvent
= [NSApp nextEventMatchingMask
: nEventMask untilDate
: [NSDate distantPast
]
765 inMode
: NSDefaultRunLoopMode dequeue
: NO
];
766 return (pEvent
!= nullptr);
769 SalFrame
* AquaSalInstance::CreateChildFrame( SystemParentData
*, SalFrameStyleFlags
/*nSalFrameStyle*/ )
774 SalFrame
* AquaSalInstance::CreateFrame( SalFrame
* pParent
, SalFrameStyleFlags nSalFrameStyle
)
776 OSX_INST_RUNINMAIN_POINTER( CreateFrame( pParent
, nSalFrameStyle
), SalFrame
* )
777 return new AquaSalFrame( pParent
, nSalFrameStyle
);
780 void AquaSalInstance::DestroyFrame( SalFrame
* pFrame
)
782 OSX_INST_RUNINMAIN( DestroyFrame( pFrame
) )
786 SalObject
* AquaSalInstance::CreateObject( SalFrame
* pParent
, SystemWindowData
* pWindowData
, bool /* bShow */ )
791 OSX_INST_RUNINMAIN_POINTER( CreateObject( pParent
, pWindowData
, false ), SalObject
* )
792 return new AquaSalObject( static_cast<AquaSalFrame
*>(pParent
), pWindowData
);
795 void AquaSalInstance::DestroyObject( SalObject
* pObject
)
797 OSX_INST_RUNINMAIN( DestroyObject( pObject
) )
801 std::unique_ptr
<SalPrinter
> AquaSalInstance::CreatePrinter( SalInfoPrinter
* pInfoPrinter
)
803 return std::unique_ptr
<SalPrinter
>(new AquaSalPrinter( dynamic_cast<AquaSalInfoPrinter
*>(pInfoPrinter
) ));
806 void AquaSalInstance::GetPrinterQueueInfo( ImplPrnQueueList
* pList
)
808 NSArray
* pNames
= [NSPrinter printerNames
];
809 NSArray
* pTypes
= [NSPrinter printerTypes
];
810 unsigned int nNameCount
= pNames
? [pNames count
] : 0;
811 unsigned int nTypeCount
= pTypes
? [pTypes count
] : 0;
812 SAL_WARN_IF( nTypeCount
!= nNameCount
, "vcl", "type count not equal to printer count" );
813 for( unsigned int i
= 0; i
< nNameCount
; i
++ )
815 NSString
* pName
= [pNames objectAtIndex
: i
];
816 NSString
* pType
= i
< nTypeCount
? [pTypes objectAtIndex
: i
] : nil
;
819 std::unique_ptr
<SalPrinterQueueInfo
> pInfo(new SalPrinterQueueInfo
);
820 pInfo
->maPrinterName
= GetOUString( pName
);
822 pInfo
->maDriver
= GetOUString( pType
);
823 pInfo
->mnStatus
= PrintQueueFlags::NONE
;
826 pList
->Add( std::move(pInfo
) );
830 // tdf#151700 Prevent the non-native LibreOffice PrintDialog from
831 // displaying by creating a fake printer if there are no printers. This
832 // will allow the LibreOffice printing code to proceed with native
833 // NSPrintOperation which will display the native print panel.
836 std::unique_ptr
<SalPrinterQueueInfo
> pInfo(new SalPrinterQueueInfo
);
837 pInfo
->maPrinterName
= getFallbackPrinterName();
838 pInfo
->mnStatus
= PrintQueueFlags::NONE
;
841 pList
->Add( std::move(pInfo
) );
845 void AquaSalInstance::GetPrinterQueueState( SalPrinterQueueInfo
* )
849 OUString
AquaSalInstance::GetDefaultPrinter()
851 // #i113170# may not be the main thread if called from UNO API
852 SalData::ensureThreadAutoreleasePool();
854 // WinSalInstance::GetDefaultPrinter() fetches current default printer
855 // on every call so do the same here
856 OUString aDefaultPrinter
;
858 NSPrintInfo
* pPI
= [NSPrintInfo sharedPrintInfo
];
859 SAL_WARN_IF( !pPI
, "vcl", "no print info" );
862 NSPrinter
* pPr
= [pPI printer
];
863 SAL_WARN_IF( !pPr
, "vcl", "no printer in default info" );
866 // Related: tdf#151700 Return the name of the fake printer if
867 // there are no printers so that the LibreOffice printing code
868 // will be able to find the fake printer returned by
869 // AquaSalInstance::GetPrinterQueueInfo()
870 NSString
* pDefName
= [pPr name
];
871 SAL_WARN_IF( !pDefName
, "vcl", "printer has no name" );
872 if ( pDefName
&& [pDefName length
])
873 aDefaultPrinter
= GetOUString( pDefName
);
875 aDefaultPrinter
= getFallbackPrinterName();
879 return aDefaultPrinter
;
882 SalInfoPrinter
* AquaSalInstance::CreateInfoPrinter( SalPrinterQueueInfo
* pQueueInfo
,
883 ImplJobSetup
* pSetupData
)
885 // #i113170# may not be the main thread if called from UNO API
886 SalData::ensureThreadAutoreleasePool();
888 SalInfoPrinter
* pNewInfoPrinter
= nullptr;
891 pNewInfoPrinter
= new AquaSalInfoPrinter( *pQueueInfo
);
893 pNewInfoPrinter
->SetPrinterData( pSetupData
);
896 return pNewInfoPrinter
;
899 void AquaSalInstance::DestroyInfoPrinter( SalInfoPrinter
* pPrinter
)
901 // #i113170# may not be the main thread if called from UNO API
902 SalData::ensureThreadAutoreleasePool();
907 OUString
AquaSalInstance::GetConnectionIdentifier()
912 // We need to re-encode file urls because osl_getFileURLFromSystemPath converts
913 // to UTF-8 before encoding non ascii characters, which is not what other apps expect.
914 static OUString
translateToExternalUrl(const OUString
& internalUrl
)
916 uno::Reference
< uno::XComponentContext
> context(
917 comphelper::getProcessComponentContext());
918 return uri::ExternalUriReferenceTranslator::create(context
)->translateToExternal(internalUrl
);
921 // #i104525# many versions of OSX have problems with some URLs:
922 // when an app requests OSX to add one of these URLs to the "Recent Items" list
923 // then this app gets killed (TextEdit, Preview, etc. and also OOo)
924 static bool isDangerousUrl( const OUString
& rUrl
)
926 // use a heuristic that detects all known cases since there is no official comment
927 // on the exact impact and root cause of the OSX bug
928 const int nLen
= rUrl
.getLength();
929 const sal_Unicode
* p
= rUrl
.getStr();
930 for( int i
= 0; i
< nLen
-3; ++i
, ++p
) {
934 if( (p
[1] == '2') && (p
[2] == '5') )
936 // escapes are considered to be UTF-8 encoded
937 // => check for invalid UTF-8 leading byte
938 if( (p
[1] != 'f') && (p
[1] != 'F') )
940 int cLowNibble
= p
[2];
941 if( (cLowNibble
>= '0' ) && (cLowNibble
<= '9'))
943 if( cLowNibble
>= 'a' )
944 cLowNibble
-= 'a' - 'A';
945 if( (cLowNibble
< 'A') || (cLowNibble
>= 'C'))
952 void AquaSalInstance::AddToRecentDocumentList(const OUString
& rFileUrl
, const OUString
& /*rMimeType*/, const OUString
& /*rDocumentService*/)
954 // Convert file URL for external use (see above)
955 OUString externalUrl
= translateToExternalUrl(rFileUrl
);
956 if( externalUrl
.isEmpty() )
957 externalUrl
= rFileUrl
;
959 if( !externalUrl
.isEmpty() && !isDangerousUrl( externalUrl
) )
961 NSString
* pString
= CreateNSString( externalUrl
);
962 NSURL
* pURL
= [NSURL URLWithString
: pString
];
966 NSDocumentController
* pCtrl
= [NSDocumentController sharedDocumentController
];
967 [pCtrl noteNewRecentDocumentURL
: pURL
];
974 SalTimer
* AquaSalInstance::CreateSalTimer()
976 return new AquaSalTimer();
979 SalSystem
* AquaSalInstance::CreateSalSystem()
981 return new AquaSalSystem();
984 std::shared_ptr
<SalBitmap
> AquaSalInstance::CreateSalBitmap()
986 #if HAVE_FEATURE_SKIA
987 if (SkiaHelper::isVCLSkiaEnabled())
988 return std::make_shared
<SkiaSalBitmap
>();
991 return std::make_shared
<QuartzSalBitmap
>();
994 OUString
AquaSalInstance::getOSVersion()
996 NSString
* versionString
= nullptr;
997 NSDictionary
* sysVersionDict
= [ NSDictionary dictionaryWithContentsOfFile
: @
"/System/Library/CoreServices/SystemVersion.plist" ];
998 if ( sysVersionDict
)
999 versionString
= [ sysVersionDict valueForKey
: @
"ProductVersion" ];
1001 OUString aVersion
= "macOS ";
1002 if ( versionString
)
1003 aVersion
+= OUString::fromUtf8( [ versionString UTF8String
] );
1005 aVersion
+= "(unknown)";
1010 CGImageRef
CreateCGImage( const Image
& rImage
)
1012 #if HAVE_FEATURE_SKIA
1013 if (SkiaHelper::isVCLSkiaEnabled())
1014 return SkiaHelper::createCGImage( rImage
);
1017 BitmapEx
aBmpEx( rImage
.GetBitmapEx() );
1018 Bitmap
aBmp( aBmpEx
.GetBitmap() );
1020 if( aBmp
.IsEmpty() || ! aBmp
.ImplGetSalBitmap() )
1023 // simple case, no transparency
1024 QuartzSalBitmap
* pSalBmp
= static_cast<QuartzSalBitmap
*>(aBmp
.ImplGetSalBitmap().get());
1029 CGImageRef xImage
= nullptr;
1030 if( !aBmpEx
.IsAlpha() )
1031 xImage
= pSalBmp
->CreateCroppedImage( 0, 0, pSalBmp
->mnWidth
, pSalBmp
->mnHeight
);
1034 AlphaMask
aAlphaMask( aBmpEx
.GetAlphaMask() );
1035 Bitmap
aMask( aAlphaMask
.GetBitmap() );
1036 QuartzSalBitmap
* pMaskBmp
= static_cast<QuartzSalBitmap
*>(aMask
.ImplGetSalBitmap().get());
1038 xImage
= pSalBmp
->CreateWithMask( *pMaskBmp
, 0, 0, pSalBmp
->mnWidth
, pSalBmp
->mnHeight
);
1040 xImage
= pSalBmp
->CreateCroppedImage( 0, 0, pSalBmp
->mnWidth
, pSalBmp
->mnHeight
);
1046 NSImage
* CreateNSImage( const Image
& rImage
)
1048 CGImageRef xImage
= CreateCGImage( rImage
);
1053 Size
aSize( rImage
.GetSizePixel() );
1054 NSImage
* pImage
= [[NSImage alloc
] initWithSize
: NSMakeSize( aSize
.Width(), aSize
.Height() )];
1057 [pImage lockFocusFlipped
:YES
];
1058 NSGraphicsContext
* pContext
= [NSGraphicsContext currentContext
];
1059 CGContextRef rCGContext
= [pContext CGContext
];
1061 const CGRect aDstRect
= { {0, 0}, { static_cast<CGFloat
>(aSize
.Width()), static_cast<CGFloat
>(aSize
.Height()) } };
1062 CGContextDrawImage( rCGContext
, aDstRect
, xImage
);
1064 [pImage unlockFocus
];
1067 CGImageRelease( xImage
);
1072 bool AquaSalInstance::SVMainHook(int* pnInit
)
1076 OUString aExeURL
, aExe
;
1077 osl_getExecutableFile( &aExeURL
.pData
);
1078 osl_getSystemPathFromFileURL( aExeURL
.pData
, &aExe
.pData
);
1079 OString
aByteExe( OUStringToOString( aExe
, osl_getThreadTextEncoding() ) );
1082 aByteExe
+= OString ( " NSAccessibilityDebugLogLevel 1" );
1083 const char* pArgv
[] = { aByteExe
.getStr(), NULL
};
1084 NSApplicationMain( 3, pArgv
);
1086 const char* pArgv
[] = { aByteExe
.getStr(), nullptr };
1087 NSApplicationMain( 1, pArgv
);
1093 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */