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 <QtInstance.hxx>
21 #include <QtInstance.moc>
23 #include <com/sun/star/lang/IllegalArgumentException.hpp>
25 #include <QtBitmap.hxx>
26 #include <QtClipboard.hxx>
28 #include <QtDragAndDrop.hxx>
29 #include <QtFilePicker.hxx>
30 #include <QtFrame.hxx>
32 #include <QtObject.hxx>
33 #include <QtOpenGLContext.hxx>
34 #include "QtSvpVirtualDevice.hxx"
35 #include <QtSystem.hxx>
36 #include <QtTimer.hxx>
37 #include <QtVirtualDevice.hxx>
39 #include <headless/svpvd.hxx>
41 #include <QtCore/QAbstractEventDispatcher>
42 #include <QtCore/QThread>
43 #include <QtGui/QScreen>
44 #include <QtWidgets/QApplication>
45 #include <QtWidgets/QWidget>
47 #include <vclpluginapi.h>
48 #include <tools/debug.hxx>
49 #include <comphelper/flagguard.hxx>
50 #include <sal/log.hxx>
51 #include <osl/process.h>
52 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && ENABLE_GSTREAMER_1_0 && QT5_HAVE_GOBJECT
53 #include <unx/gstsink.hxx>
55 #include <headless/svpbmp.hxx>
58 #include <condition_variable>
61 #include <QtCore/QtPlugin>
62 Q_IMPORT_PLUGIN(QWasmIntegrationPlugin
)
67 /// TODO: not much Qt specific here? could be generalised, esp. for OSX...
68 /// this subclass allows for the transfer of a closure for running on the main
69 /// thread, to handle all the thread affine stuff in Qt; the SolarMutex is
70 /// "loaned" to the main thread for the execution of the closure.
71 /// @note it doesn't work to just use "emit" and signals/slots to move calls to
72 /// the main thread, because the other thread has the SolarMutex; the other
73 /// thread (typically) cannot release SolarMutex, because then the main thread
74 /// will handle all sorts of events and whatnot; this design ensures that the
75 /// main thread only runs the passed closure (unless the closure releases
76 /// SolarMutex itself, which should probably be avoided).
77 class QtYieldMutex
: public SalYieldMutex
80 /// flag only accessed on main thread:
81 /// main thread has "borrowed" SolarMutex from another thread
82 bool m_bNoYieldLock
= false;
83 /// members for communication from non-main thread to main thread
84 std::mutex m_RunInMainMutex
;
85 std::condition_variable m_InMainCondition
;
86 bool m_isWakeUpMain
= false;
87 std::function
<void()> m_Closure
; ///< code for main thread to run
88 /// members for communication from main thread to non-main thread
89 std::condition_variable m_ResultCondition
;
90 bool m_isResultReady
= false;
92 virtual bool IsCurrentThread() const override
;
93 virtual void doAcquire(sal_uInt32 nLockCount
) override
;
94 virtual sal_uInt32
doRelease(bool const bUnlockAll
) override
;
98 bool QtYieldMutex::IsCurrentThread() const
100 auto const* pSalInst(static_cast<QtInstance
const*>(GetSalData()->m_pInstance
));
102 if (pSalInst
->IsMainThread() && m_bNoYieldLock
)
104 return true; // main thread has borrowed SolarMutex
106 return SalYieldMutex::IsCurrentThread();
109 void QtYieldMutex::doAcquire(sal_uInt32 nLockCount
)
111 auto const* pSalInst(static_cast<QtInstance
const*>(GetSalData()->m_pInstance
));
113 if (!pSalInst
->IsMainThread())
115 SalYieldMutex::doAcquire(nLockCount
);
120 return; // special case for main thread: borrowed from other thread
122 do // main thread acquire...
124 std::function
<void()> func
; // copy of closure on thread stack
126 std::unique_lock
<std::mutex
> g(m_RunInMainMutex
);
127 if (m_aMutex
.tryToAcquire())
129 // if there's a closure, the other thread holds m_aMutex
131 m_isWakeUpMain
= false;
132 --nLockCount
; // have acquired once!
136 m_InMainCondition
.wait(g
, [this]() { return m_isWakeUpMain
; });
137 m_isWakeUpMain
= false;
138 std::swap(func
, m_Closure
);
142 assert(!m_bNoYieldLock
);
143 m_bNoYieldLock
= true; // execute closure with borrowed SolarMutex
145 m_bNoYieldLock
= false;
146 std::scoped_lock
<std::mutex
> g(m_RunInMainMutex
);
147 assert(!m_isResultReady
);
148 m_isResultReady
= true;
149 m_ResultCondition
.notify_all(); // unblock other thread
152 SalYieldMutex::doAcquire(nLockCount
);
155 sal_uInt32
QtYieldMutex::doRelease(bool const bUnlockAll
)
157 auto const* pSalInst(static_cast<QtInstance
const*>(GetSalData()->m_pInstance
));
159 if (pSalInst
->IsMainThread() && m_bNoYieldLock
)
161 return 1; // dummy value
164 std::scoped_lock
<std::mutex
> g(m_RunInMainMutex
);
165 // read m_nCount before doRelease (it's guarded by m_aMutex)
166 bool const isReleased(bUnlockAll
|| m_nCount
== 1);
167 sal_uInt32 nCount
= SalYieldMutex::doRelease(bUnlockAll
);
168 if (isReleased
&& !pSalInst
->IsMainThread())
170 m_isWakeUpMain
= true;
171 m_InMainCondition
.notify_all(); // unblock main thread
176 // this could be abstracted to be independent of Qt by passing in the
177 // event-trigger as another function parameter...
178 // it could also be a template of the return type, then it could return the
179 // result of func... but then how to handle the result in doAcquire?
180 void QtInstance::RunInMainThread(std::function
<void()> func
)
182 DBG_TESTSOLARMUTEX();
189 QtYieldMutex
* const pMutex(static_cast<QtYieldMutex
*>(GetYieldMutex()));
191 std::scoped_lock
<std::mutex
> g(pMutex
->m_RunInMainMutex
);
192 assert(!pMutex
->m_Closure
);
193 pMutex
->m_Closure
= func
;
194 // unblock main thread in case it is blocked on condition
195 pMutex
->m_isWakeUpMain
= true;
196 pMutex
->m_InMainCondition
.notify_all();
199 TriggerUserEventProcessing();
201 std::unique_lock
<std::mutex
> g(pMutex
->m_RunInMainMutex
);
202 pMutex
->m_ResultCondition
.wait(g
, [pMutex
]() { return pMutex
->m_isResultReady
; });
203 pMutex
->m_isResultReady
= false;
207 OUString
QtInstance::constructToolkitID(std::u16string_view sTKname
)
209 OUString
sID(sTKname
+ OUString::Concat(u
" ("));
214 sID
+= toOUString(QGuiApplication::platformName()) + OUString::Concat(u
")");
218 QtInstance::QtInstance(std::unique_ptr
<QApplication
>& pQApp
, bool bUseCairo
)
219 : SalGenericInstance(std::make_unique
<QtYieldMutex
>())
220 , m_bUseCairo(bUseCairo
)
223 , m_pQApplication(std::move(pQApp
))
224 , m_aUpdateStyleTimer("vcl::qt5 m_aUpdateStyleTimer")
225 , m_bUpdateFonts(false)
226 , m_pActivePopup(nullptr)
228 ImplSVData
* pSVData
= ImplGetSVData();
229 const OUString sToolkit
= "qt" + OUString::number(QT_VERSION_MAJOR
);
230 pSVData
->maAppData
.mxToolkitName
= constructToolkitID(sToolkit
);
232 // this one needs to be blocking, so that the handling in main thread
233 // is processed before the thread emitting the signal continues
234 connect(this, SIGNAL(ImplYieldSignal(bool, bool)), this, SLOT(ImplYield(bool, bool)),
235 Qt::BlockingQueuedConnection
);
237 // this one needs to be queued non-blocking
238 // in order to have this event arriving to correct event processing loop
239 connect(this, &QtInstance::deleteObjectLaterSignal
, this,
240 [](QObject
* pObject
) { QtInstance::deleteObjectLater(pObject
); }, Qt::QueuedConnection
);
242 m_aUpdateStyleTimer
.SetTimeout(50);
243 m_aUpdateStyleTimer
.SetInvokeHandler(LINK(this, QtInstance
, updateStyleHdl
));
245 QAbstractEventDispatcher
* dispatcher
= QAbstractEventDispatcher::instance(qApp
->thread());
246 connect(dispatcher
, &QAbstractEventDispatcher::awake
, this, [this]() { m_bSleeping
= false; });
247 connect(dispatcher
, &QAbstractEventDispatcher::aboutToBlock
, this,
248 [this]() { m_bSleeping
= true; });
250 connect(QGuiApplication::inputMethod(), &QInputMethod::localeChanged
, this,
251 &QtInstance::localeChanged
);
253 for (const QScreen
* pCurScreen
: QApplication::screens())
254 connectQScreenSignals(pCurScreen
);
255 connect(qApp
, &QGuiApplication::primaryScreenChanged
, this, &QtInstance::primaryScreenChanged
);
256 connect(qApp
, &QGuiApplication::screenAdded
, this, &QtInstance::screenAdded
);
257 connect(qApp
, &QGuiApplication::screenRemoved
, this, &QtInstance::screenRemoved
);
260 m_bSupportsOpenGL
= true;
264 QtInstance::~QtInstance()
266 // force freeing the QApplication before freeing the arguments,
267 // as it uses references to the provided arguments!
268 m_pQApplication
.reset();
271 void QtInstance::AfterAppInit()
273 // set the default application icon via desktop file just on Wayland,
274 // as this otherwise overrides the individual desktop icons on X11.
275 if (QGuiApplication::platformName() == "wayland")
276 QGuiApplication::setDesktopFileName(QStringLiteral("libreoffice-startcenter.desktop"));
277 QGuiApplication::setLayoutDirection(AllSettings::GetLayoutRTL() ? Qt::RightToLeft
281 void QtInstance::localeChanged()
283 SolarMutexGuard aGuard
;
284 const vcl::Window
* pFocusWindow
= Application::GetFocusWindow();
285 SalFrame
* const pFocusFrame
= pFocusWindow
? pFocusWindow
->ImplGetFrame() : nullptr;
289 const LanguageTag
aTag(
290 toOUString(QGuiApplication::inputMethod()->locale().name().replace("_", "-")));
291 static_cast<QtFrame
*>(pFocusFrame
)->setInputLanguage(aTag
.getLanguageType());
294 void QtInstance::deleteObjectLater(QObject
* pObject
) { pObject
->deleteLater(); }
296 SalFrame
* QtInstance::CreateChildFrame(SystemParentData
* /*pParent*/, SalFrameStyleFlags nStyle
)
298 SalFrame
* pRet(nullptr);
299 RunInMainThread([&, this]() { pRet
= new QtFrame(nullptr, nStyle
, useCairo()); });
304 SalFrame
* QtInstance::CreateFrame(SalFrame
* pParent
, SalFrameStyleFlags nStyle
)
306 assert(!pParent
|| dynamic_cast<QtFrame
*>(pParent
));
308 SalFrame
* pRet(nullptr);
310 [&, this]() { pRet
= new QtFrame(static_cast<QtFrame
*>(pParent
), nStyle
, useCairo()); });
315 void QtInstance::DestroyFrame(SalFrame
* pFrame
)
319 assert(dynamic_cast<QtFrame
*>(pFrame
));
320 Q_EMIT
deleteObjectLaterSignal(static_cast<QtFrame
*>(pFrame
));
324 SalObject
* QtInstance::CreateObject(SalFrame
* pParent
, SystemWindowData
*, bool bShow
)
326 assert(!pParent
|| dynamic_cast<QtFrame
*>(pParent
));
328 SalObject
* pRet(nullptr);
329 RunInMainThread([&]() { pRet
= new QtObject(static_cast<QtFrame
*>(pParent
), bShow
); });
334 void QtInstance::DestroyObject(SalObject
* pObject
)
338 assert(dynamic_cast<QtObject
*>(pObject
));
339 Q_EMIT
deleteObjectLaterSignal(static_cast<QtObject
*>(pObject
));
343 std::unique_ptr
<SalVirtualDevice
>
344 QtInstance::CreateVirtualDevice(SalGraphics
& rGraphics
, tools::Long
& nDX
, tools::Long
& nDY
,
345 DeviceFormat
/*eFormat*/, const SystemGraphicsData
* pGd
)
349 SvpSalGraphics
* pSvpSalGraphics
= dynamic_cast<QtSvpGraphics
*>(&rGraphics
);
350 assert(pSvpSalGraphics
);
351 // tdf#127529 see SvpSalInstance::CreateVirtualDevice for the rare case of a non-null pPreExistingTarget
352 cairo_surface_t
* pPreExistingTarget
353 = pGd
? static_cast<cairo_surface_t
*>(pGd
->pSurface
) : nullptr;
354 std::unique_ptr
<SalVirtualDevice
> pVD(
355 new QtSvpVirtualDevice(pSvpSalGraphics
->getSurface(), pPreExistingTarget
));
356 pVD
->SetSize(nDX
, nDY
);
361 std::unique_ptr
<SalVirtualDevice
> pVD(new QtVirtualDevice(/*scale*/ 1));
362 pVD
->SetSize(nDX
, nDY
);
367 std::unique_ptr
<SalMenu
> QtInstance::CreateMenu(bool bMenuBar
, Menu
* pVCLMenu
)
369 SolarMutexGuard aGuard
;
370 std::unique_ptr
<SalMenu
> pRet
;
371 RunInMainThread([&pRet
, bMenuBar
, pVCLMenu
]() {
372 QtMenu
* pSalMenu
= new QtMenu(bMenuBar
);
373 pRet
.reset(pSalMenu
);
374 pSalMenu
->SetMenu(pVCLMenu
);
380 std::unique_ptr
<SalMenuItem
> QtInstance::CreateMenuItem(const SalItemParams
& rItemData
)
382 return std::unique_ptr
<SalMenuItem
>(new QtMenuItem(&rItemData
));
385 SalTimer
* QtInstance::CreateSalTimer()
387 m_pTimer
= new QtTimer();
391 SalSystem
* QtInstance::CreateSalSystem() { return new QtSystem
; }
393 std::shared_ptr
<SalBitmap
> QtInstance::CreateSalBitmap()
396 return std::make_shared
<SvpSalBitmap
>();
398 return std::make_shared
<QtBitmap
>();
401 bool QtInstance::ImplYield(bool bWait
, bool bHandleAllCurrentEvents
)
403 // Re-acquire the guard for user events when called via Q_EMIT ImplYieldSignal
404 SolarMutexGuard aGuard
;
405 bool wasEvent
= DispatchUserEvents(bHandleAllCurrentEvents
);
406 if (!bHandleAllCurrentEvents
&& wasEvent
)
410 * Quoting the Qt docs: [QAbstractEventDispatcher::processEvents] processes
411 * pending events that match flags until there are no more events to process.
413 SolarMutexReleaser aReleaser
;
414 QAbstractEventDispatcher
* dispatcher
= QAbstractEventDispatcher::instance(qApp
->thread());
415 if (bWait
&& !wasEvent
)
416 wasEvent
= dispatcher
->processEvents(QEventLoop::WaitForMoreEvents
);
418 wasEvent
= dispatcher
->processEvents(QEventLoop::AllEvents
) || wasEvent
;
422 bool QtInstance::DoYield(bool bWait
, bool bHandleAllCurrentEvents
)
424 bool bWasEvent
= false;
425 if (qApp
->thread() == QThread::currentThread())
427 bWasEvent
= ImplYield(bWait
, bHandleAllCurrentEvents
);
429 m_aWaitingYieldCond
.set();
434 SolarMutexReleaser aReleaser
;
435 bWasEvent
= Q_EMIT
ImplYieldSignal(false, bHandleAllCurrentEvents
);
437 if (!bWasEvent
&& bWait
)
439 m_aWaitingYieldCond
.reset();
440 SolarMutexReleaser aReleaser
;
441 m_aWaitingYieldCond
.wait();
448 bool QtInstance::AnyInput(VclInputFlags nType
)
450 bool bResult
= false;
451 if (nType
& VclInputFlags::TIMER
)
452 bResult
|= (m_pTimer
&& m_pTimer
->remainingTime() == 0);
453 if (nType
& VclInputFlags::OTHER
)
454 bResult
|= !m_bSleeping
;
458 OUString
QtInstance::GetConnectionIdentifier() { return OUString(); }
460 void QtInstance::AddToRecentDocumentList(const OUString
&, const OUString
&, const OUString
&) {}
463 OpenGLContext
* QtInstance::CreateOpenGLContext() { return new QtOpenGLContext
; }
466 bool QtInstance::IsMainThread() const
468 return !qApp
|| (qApp
->thread() == QThread::currentThread());
471 void QtInstance::TriggerUserEventProcessing()
473 QAbstractEventDispatcher
* dispatcher
= QAbstractEventDispatcher::instance(qApp
->thread());
474 dispatcher
->wakeUp();
477 void QtInstance::ProcessEvent(SalUserEvent aEvent
)
479 aEvent
.m_pFrame
->CallCallback(aEvent
.m_nEvent
, aEvent
.m_pData
);
482 rtl::Reference
<QtFilePicker
>
483 QtInstance::createPicker(css::uno::Reference
<css::uno::XComponentContext
> const& context
,
484 QFileDialog::FileMode eMode
)
489 rtl::Reference
<QtFilePicker
> pPicker
;
490 RunInMainThread([&, this]() { pPicker
= createPicker(context
, eMode
); });
495 return new QtFilePicker(context
, eMode
);
498 css::uno::Reference
<css::ui::dialogs::XFilePicker2
>
499 QtInstance::createFilePicker(const css::uno::Reference
<css::uno::XComponentContext
>& context
)
501 return css::uno::Reference
<css::ui::dialogs::XFilePicker2
>(
502 createPicker(context
, QFileDialog::ExistingFile
));
505 css::uno::Reference
<css::ui::dialogs::XFolderPicker2
>
506 QtInstance::createFolderPicker(const css::uno::Reference
<css::uno::XComponentContext
>& context
)
508 return css::uno::Reference
<css::ui::dialogs::XFolderPicker2
>(
509 createPicker(context
, QFileDialog::Directory
));
512 css::uno::Reference
<css::uno::XInterface
>
513 QtInstance::CreateClipboard(const css::uno::Sequence
<css::uno::Any
>& arguments
)
516 if (arguments
.getLength() == 0)
520 else if (arguments
.getLength() != 1 || !(arguments
[0] >>= sel
))
522 throw css::lang::IllegalArgumentException("bad QtInstance::CreateClipboard arguments",
523 css::uno::Reference
<css::uno::XInterface
>(), -1);
526 // This could also use RunInMain, but SolarMutexGuard is enough
527 // since at this point we're not accessing the clipboard, just get the
528 // accessor to the clipboard.
529 SolarMutexGuard aGuard
;
531 auto it
= m_aClipboards
.find(sel
);
532 if (it
!= m_aClipboards
.end())
535 css::uno::Reference
<css::uno::XInterface
> xClipboard
= QtClipboard::create(sel
);
537 m_aClipboards
[sel
] = xClipboard
;
542 css::uno::Reference
<css::uno::XInterface
> QtInstance::CreateDragSource()
544 return css::uno::Reference
<css::uno::XInterface
>(
545 static_cast<cppu::OWeakObject
*>(new QtDragSource()));
548 css::uno::Reference
<css::uno::XInterface
> QtInstance::CreateDropTarget()
550 return css::uno::Reference
<css::uno::XInterface
>(
551 static_cast<cppu::OWeakObject
*>(new QtDropTarget()));
554 IMPL_LINK_NOARG(QtInstance
, updateStyleHdl
, Timer
*, void)
556 SolarMutexGuard aGuard
;
557 SalFrame
* pFrame
= anyFrame();
560 pFrame
->CallCallback(SalEvent::SettingsChanged
, nullptr);
563 pFrame
->CallCallback(SalEvent::FontChanged
, nullptr);
564 m_bUpdateFonts
= false;
569 void QtInstance::UpdateStyle(bool bFontsChanged
)
572 m_bUpdateFonts
= true;
573 if (!m_aUpdateStyleTimer
.IsActive())
574 m_aUpdateStyleTimer
.Start();
577 void* QtInstance::CreateGStreamerSink(const SystemChildWindow
* pWindow
)
579 // As of 2021-09, qt-gstreamer is unmaintained and there is no Qt 6 video sink
580 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && ENABLE_GSTREAMER_1_0 && QT5_HAVE_GOBJECT
581 auto pSymbol
= gstElementFactoryNameSymbol();
585 const SystemEnvData
* pEnvData
= pWindow
->GetSystemData();
589 if (pEnvData
->platform
!= SystemEnvData::Platform::Wayland
)
592 GstElement
* pVideosink
= pSymbol("qwidget5videosink", "qwidget5videosink");
595 QWidget
* pQWidget
= static_cast<QWidget
*>(pEnvData
->pWidget
);
596 g_object_set(G_OBJECT(pVideosink
), "widget", pQWidget
, nullptr);
600 SAL_WARN("vcl.qt", "Couldn't initialize qwidget5videosink."
601 " Video playback might not work as expected."
602 " Please install Qt5 packages for QtGStreamer.");
603 // with no videosink explicitly set, GStreamer will open its own (misplaced) window(s) to display video
613 void QtInstance::connectQScreenSignals(const QScreen
* pScreen
)
615 connect(pScreen
, &QScreen::orientationChanged
, this, &QtInstance::orientationChanged
);
616 connect(pScreen
, &QScreen::virtualGeometryChanged
, this, &QtInstance::virtualGeometryChanged
);
619 void QtInstance::notifyDisplayChanged()
621 SolarMutexGuard aGuard
;
622 SalFrame
* pAnyFrame
= anyFrame();
624 pAnyFrame
->CallCallback(SalEvent::DisplayChanged
, nullptr);
627 void QtInstance::orientationChanged(Qt::ScreenOrientation
) { notifyDisplayChanged(); }
629 void QtInstance::primaryScreenChanged(QScreen
*) { notifyDisplayChanged(); }
631 void QtInstance::screenAdded(QScreen
* pScreen
)
633 connectQScreenSignals(pScreen
);
634 if (QApplication::screens().size() == 1)
635 notifyDisplayChanged();
638 void QtInstance::screenRemoved(QScreen
*) { notifyDisplayChanged(); }
640 void QtInstance::virtualGeometryChanged(const QRect
&) { notifyDisplayChanged(); }
642 void QtInstance::AllocFakeCmdlineArgs(std::unique_ptr
<char* []>& rFakeArgv
,
643 std::unique_ptr
<int>& rFakeArgc
,
644 std::vector
<FreeableCStr
>& rFakeArgvFreeable
)
646 OString
aVersion(qVersion());
647 SAL_INFO("vcl.qt", "qt version string is " << aVersion
);
649 const sal_uInt32 nParams
= osl_getCommandArgCount();
650 sal_uInt32 nDisplayValueIdx
= 0;
651 OUString aParam
, aBin
;
653 for (sal_uInt32 nIdx
= 0; nIdx
< nParams
; ++nIdx
)
655 osl_getCommandArg(nIdx
, &aParam
.pData
);
656 if (aParam
!= "-display")
659 nDisplayValueIdx
= nIdx
;
662 osl_getExecutableFile(&aParam
.pData
);
663 osl_getSystemPathFromFileURL(aParam
.pData
, &aBin
.pData
);
664 OString aExec
= OUStringToOString(aBin
, osl_getThreadTextEncoding());
666 std::vector
<FreeableCStr
> aFakeArgvFreeable
;
667 aFakeArgvFreeable
.reserve(4);
668 aFakeArgvFreeable
.emplace_back(strdup(aExec
.getStr()));
669 aFakeArgvFreeable
.emplace_back(strdup("--nocrashhandler"));
670 if (nDisplayValueIdx
)
672 aFakeArgvFreeable
.emplace_back(strdup("-display"));
673 osl_getCommandArg(nDisplayValueIdx
, &aParam
.pData
);
674 OString aDisplay
= OUStringToOString(aParam
, osl_getThreadTextEncoding());
675 aFakeArgvFreeable
.emplace_back(strdup(aDisplay
.getStr()));
677 rFakeArgvFreeable
.swap(aFakeArgvFreeable
);
679 const int nFakeArgc
= rFakeArgvFreeable
.size();
680 rFakeArgv
.reset(new char*[nFakeArgc
]);
681 for (int i
= 0; i
< nFakeArgc
; i
++)
682 rFakeArgv
[i
] = rFakeArgvFreeable
[i
].get();
684 rFakeArgc
.reset(new int);
685 *rFakeArgc
= nFakeArgc
;
688 void QtInstance::MoveFakeCmdlineArgs(std::unique_ptr
<char* []>& rFakeArgv
,
689 std::unique_ptr
<int>& rFakeArgc
,
690 std::vector
<FreeableCStr
>& rFakeArgvFreeable
)
692 m_pFakeArgv
= std::move(rFakeArgv
);
693 m_pFakeArgc
= std::move(rFakeArgc
);
694 m_pFakeArgvFreeable
.swap(rFakeArgvFreeable
);
697 std::unique_ptr
<QApplication
> QtInstance::CreateQApplication(int& nArgc
, char** pArgv
)
699 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
700 // for Qt 6, setting Qt::AA_EnableHighDpiScaling and Qt::AA_UseHighDpiPixmaps
701 // is deprecated, they're always enabled
702 QApplication::setAttribute(Qt::AA_EnableHighDpiScaling
);
703 // for scaled icons in the native menus
704 QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps
);
707 FreeableCStr session_manager
;
708 if (getenv("SESSION_MANAGER") != nullptr)
710 session_manager
.reset(strdup(getenv("SESSION_MANAGER")));
711 unsetenv("SESSION_MANAGER");
714 std::unique_ptr
<QApplication
> pQApp
= std::make_unique
<QApplication
>(nArgc
, pArgv
);
716 if (session_manager
!= nullptr)
718 // coverity[tainted_string] - trusted source for setenv
719 setenv("SESSION_MANAGER", session_manager
.get(), 1);
722 QApplication::setQuitOnLastWindowClosed(false);
726 void QtInstance::setActivePopup(QtFrame
* pFrame
)
728 assert(!pFrame
|| pFrame
->isPopup());
729 m_pActivePopup
= pFrame
;
733 VCLPLUG_QT_PUBLIC SalInstance
* create_SalInstance()
735 static const bool bUseCairo
= (nullptr != getenv("SAL_VCL_QT5_USE_CAIRO"));
737 std::unique_ptr
<char* []> pFakeArgv
;
738 std::unique_ptr
<int> pFakeArgc
;
739 std::vector
<FreeableCStr
> aFakeArgvFreeable
;
740 QtInstance::AllocFakeCmdlineArgs(pFakeArgv
, pFakeArgc
, aFakeArgvFreeable
);
742 std::unique_ptr
<QApplication
> pQApp
743 = QtInstance::CreateQApplication(*pFakeArgc
, pFakeArgv
.get());
745 QtInstance
* pInstance
= new QtInstance(pQApp
, bUseCairo
);
746 pInstance
->MoveFakeCmdlineArgs(pFakeArgv
, pFakeArgc
, aFakeArgvFreeable
);
748 new QtData(pInstance
);
754 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */