Fix CppunitTest_sc_subsequent_filters_test2 on non-default DPI
[LibreOffice.git] / vcl / qt5 / QtInstance.cxx
blob9c4d3def970e13fb252c268217d0031b35093537
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <QtInstance.hxx>
21 #include <QtInstance.moc>
23 #include <com/sun/star/lang/IllegalArgumentException.hpp>
25 #include <QtBitmap.hxx>
26 #include <QtClipboard.hxx>
27 #include <QtData.hxx>
28 #include <QtDragAndDrop.hxx>
29 #include <QtFilePicker.hxx>
30 #include <QtFrame.hxx>
31 #include <QtMenu.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/QLibraryInfo>
43 #include <QtCore/QThread>
44 #include <QtGui/QScreen>
45 #include <QtWidgets/QApplication>
46 #include <QtWidgets/QWidget>
48 #include <vclpluginapi.h>
49 #include <tools/debug.hxx>
50 #include <comphelper/flagguard.hxx>
51 #include <dndhelper.hxx>
52 #include <vcl/sysdata.hxx>
53 #include <sal/log.hxx>
54 #include <osl/process.h>
55 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && ENABLE_GSTREAMER_1_0 && QT5_HAVE_GOBJECT
56 #include <unx/gstsink.hxx>
57 #endif
58 #include <headless/svpbmp.hxx>
60 #include <mutex>
61 #include <condition_variable>
63 #ifdef EMSCRIPTEN
64 #include <QtCore/QtPlugin>
65 Q_IMPORT_PLUGIN(QWasmIntegrationPlugin)
66 #endif
68 namespace
70 /// TODO: not much Qt specific here? could be generalised, esp. for OSX...
71 /// this subclass allows for the transfer of a closure for running on the main
72 /// thread, to handle all the thread affine stuff in Qt; the SolarMutex is
73 /// "loaned" to the main thread for the execution of the closure.
74 /// @note it doesn't work to just use "emit" and signals/slots to move calls to
75 /// the main thread, because the other thread has the SolarMutex; the other
76 /// thread (typically) cannot release SolarMutex, because then the main thread
77 /// will handle all sorts of events and whatnot; this design ensures that the
78 /// main thread only runs the passed closure (unless the closure releases
79 /// SolarMutex itself, which should probably be avoided).
80 class QtYieldMutex : public SalYieldMutex
82 public:
83 /// flag only accessed on main thread:
84 /// main thread has "borrowed" SolarMutex from another thread
85 bool m_bNoYieldLock = false;
86 /// members for communication from non-main thread to main thread
87 std::mutex m_RunInMainMutex;
88 std::condition_variable m_InMainCondition;
89 bool m_isWakeUpMain = false;
90 std::function<void()> m_Closure; ///< code for main thread to run
91 /// members for communication from main thread to non-main thread
92 std::condition_variable m_ResultCondition;
93 bool m_isResultReady = false;
95 virtual bool IsCurrentThread() const override;
96 virtual void doAcquire(sal_uInt32 nLockCount) override;
97 virtual sal_uInt32 doRelease(bool const bUnlockAll) override;
101 bool QtYieldMutex::IsCurrentThread() const
103 auto const* pSalInst(GetQtInstance());
104 assert(pSalInst);
105 if (pSalInst->IsMainThread() && m_bNoYieldLock)
107 return true; // main thread has borrowed SolarMutex
109 return SalYieldMutex::IsCurrentThread();
112 void QtYieldMutex::doAcquire(sal_uInt32 nLockCount)
114 auto const* pSalInst(GetQtInstance());
115 assert(pSalInst);
116 if (!pSalInst->IsMainThread())
118 SalYieldMutex::doAcquire(nLockCount);
119 return;
121 if (m_bNoYieldLock)
123 return; // special case for main thread: borrowed from other thread
125 do // main thread acquire...
127 std::function<void()> func; // copy of closure on thread stack
129 std::unique_lock<std::mutex> g(m_RunInMainMutex);
130 if (m_aMutex.tryToAcquire())
132 // if there's a closure, the other thread holds m_aMutex
133 assert(!m_Closure);
134 m_isWakeUpMain = false;
135 --nLockCount; // have acquired once!
136 ++m_nCount;
137 break;
139 m_InMainCondition.wait(g, [this]() { return m_isWakeUpMain; });
140 m_isWakeUpMain = false;
141 std::swap(func, m_Closure);
143 if (func)
145 assert(!m_bNoYieldLock);
146 m_bNoYieldLock = true; // execute closure with borrowed SolarMutex
147 func();
148 m_bNoYieldLock = false;
149 std::scoped_lock<std::mutex> g(m_RunInMainMutex);
150 assert(!m_isResultReady);
151 m_isResultReady = true;
152 m_ResultCondition.notify_all(); // unblock other thread
154 } while (true);
155 SalYieldMutex::doAcquire(nLockCount);
158 sal_uInt32 QtYieldMutex::doRelease(bool const bUnlockAll)
160 auto const* pSalInst(GetQtInstance());
161 assert(pSalInst);
162 if (pSalInst->IsMainThread() && m_bNoYieldLock)
164 return 1; // dummy value
167 std::scoped_lock<std::mutex> g(m_RunInMainMutex);
168 // read m_nCount before doRelease (it's guarded by m_aMutex)
169 bool const isReleased(bUnlockAll || m_nCount == 1);
170 sal_uInt32 nCount = SalYieldMutex::doRelease(bUnlockAll);
171 if (isReleased && !pSalInst->IsMainThread())
173 m_isWakeUpMain = true;
174 m_InMainCondition.notify_all(); // unblock main thread
176 return nCount;
179 // this could be abstracted to be independent of Qt by passing in the
180 // event-trigger as another function parameter...
181 // it could also be a template of the return type, then it could return the
182 // result of func... but then how to handle the result in doAcquire?
183 void QtInstance::RunInMainThread(std::function<void()> func)
185 DBG_TESTSOLARMUTEX();
186 if (IsMainThread())
188 func();
189 return;
192 QtYieldMutex* const pMutex(static_cast<QtYieldMutex*>(GetYieldMutex()));
194 std::scoped_lock<std::mutex> g(pMutex->m_RunInMainMutex);
195 assert(!pMutex->m_Closure);
196 pMutex->m_Closure = func;
197 // unblock main thread in case it is blocked on condition
198 pMutex->m_isWakeUpMain = true;
199 pMutex->m_InMainCondition.notify_all();
202 TriggerUserEventProcessing();
204 std::unique_lock<std::mutex> g(pMutex->m_RunInMainMutex);
205 pMutex->m_ResultCondition.wait(g, [pMutex]() { return pMutex->m_isResultReady; });
206 pMutex->m_isResultReady = false;
210 OUString QtInstance::constructToolkitID(std::u16string_view sTKname)
212 OUString sID(sTKname + OUString::Concat(u" ("));
213 if (m_bUseCairo)
214 sID += "cairo+";
215 else
216 sID += "qfont+";
217 sID += toOUString(QGuiApplication::platformName()) + OUString::Concat(u")");
218 return sID;
221 QtInstance::QtInstance(std::unique_ptr<QApplication>& pQApp, bool bUseCairo)
222 : SalGenericInstance(std::make_unique<QtYieldMutex>())
223 , m_bUseCairo(bUseCairo)
224 , m_pTimer(nullptr)
225 , m_bSleeping(false)
226 , m_pQApplication(std::move(pQApp))
227 , m_aUpdateStyleTimer("vcl::qt5 m_aUpdateStyleTimer")
228 , m_bUpdateFonts(false)
229 , m_pActivePopup(nullptr)
231 ImplSVData* pSVData = ImplGetSVData();
232 const OUString sToolkit = "qt" + OUString::number(QT_VERSION_MAJOR);
233 pSVData->maAppData.mxToolkitName = constructToolkitID(sToolkit);
235 // this one needs to be blocking, so that the handling in main thread
236 // is processed before the thread emitting the signal continues
237 connect(this, SIGNAL(ImplYieldSignal(bool, bool)), this, SLOT(ImplYield(bool, bool)),
238 Qt::BlockingQueuedConnection);
240 // this one needs to be queued non-blocking
241 // in order to have this event arriving to correct event processing loop
242 connect(this, &QtInstance::deleteObjectLaterSignal, this,
243 [](QObject* pObject) { QtInstance::deleteObjectLater(pObject); }, Qt::QueuedConnection);
245 m_aUpdateStyleTimer.SetTimeout(50);
246 m_aUpdateStyleTimer.SetInvokeHandler(LINK(this, QtInstance, updateStyleHdl));
248 QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance(qApp->thread());
249 connect(dispatcher, &QAbstractEventDispatcher::awake, this, [this]() { m_bSleeping = false; });
250 connect(dispatcher, &QAbstractEventDispatcher::aboutToBlock, this,
251 [this]() { m_bSleeping = true; });
253 connect(QGuiApplication::inputMethod(), &QInputMethod::localeChanged, this,
254 &QtInstance::localeChanged);
256 for (const QScreen* pCurScreen : QApplication::screens())
257 connectQScreenSignals(pCurScreen);
258 connect(qApp, &QGuiApplication::primaryScreenChanged, this, &QtInstance::primaryScreenChanged);
259 connect(qApp, &QGuiApplication::screenAdded, this, &QtInstance::screenAdded);
260 connect(qApp, &QGuiApplication::screenRemoved, this, &QtInstance::screenRemoved);
262 #ifndef EMSCRIPTEN
263 m_bSupportsOpenGL = true;
264 #else
265 ImplGetSVData()->maAppData.m_bUseSystemLoop = true;
266 #endif
269 QtInstance::~QtInstance()
271 // force freeing the QApplication before freeing the arguments,
272 // as it uses references to the provided arguments!
273 m_pQApplication.reset();
276 void QtInstance::AfterAppInit()
278 // set the default application icon via desktop file just on Wayland,
279 // as this otherwise overrides the individual desktop icons on X11.
280 if (QGuiApplication::platformName() == "wayland")
281 QGuiApplication::setDesktopFileName(QStringLiteral("libreoffice-startcenter.desktop"));
282 QGuiApplication::setLayoutDirection(AllSettings::GetLayoutRTL() ? Qt::RightToLeft
283 : Qt::LeftToRight);
286 void QtInstance::localeChanged()
288 SolarMutexGuard aGuard;
289 const vcl::Window* pFocusWindow = Application::GetFocusWindow();
290 SalFrame* const pFocusFrame = pFocusWindow ? pFocusWindow->ImplGetFrame() : nullptr;
291 if (!pFocusFrame)
292 return;
294 const LanguageTag aTag(
295 toOUString(QGuiApplication::inputMethod()->locale().name().replace("_", "-")));
296 static_cast<QtFrame*>(pFocusFrame)->setInputLanguage(aTag.getLanguageType());
299 void QtInstance::deleteObjectLater(QObject* pObject) { pObject->deleteLater(); }
301 SalFrame* QtInstance::CreateChildFrame(SystemParentData* /*pParent*/, SalFrameStyleFlags nStyle)
303 SalFrame* pRet(nullptr);
304 RunInMainThread([&, this]() { pRet = new QtFrame(nullptr, nStyle, useCairo()); });
305 assert(pRet);
306 return pRet;
309 SalFrame* QtInstance::CreateFrame(SalFrame* pParent, SalFrameStyleFlags nStyle)
311 assert(!pParent || dynamic_cast<QtFrame*>(pParent));
313 SalFrame* pRet(nullptr);
314 RunInMainThread(
315 [&, this]() { pRet = new QtFrame(static_cast<QtFrame*>(pParent), nStyle, useCairo()); });
316 assert(pRet);
317 return pRet;
320 void QtInstance::DestroyFrame(SalFrame* pFrame)
322 if (pFrame)
324 assert(dynamic_cast<QtFrame*>(pFrame));
325 Q_EMIT deleteObjectLaterSignal(static_cast<QtFrame*>(pFrame));
329 SalObject* QtInstance::CreateObject(SalFrame* pParent, SystemWindowData*, bool bShow)
331 assert(!pParent || dynamic_cast<QtFrame*>(pParent));
333 SalObject* pRet(nullptr);
334 RunInMainThread([&]() { pRet = new QtObject(static_cast<QtFrame*>(pParent), bShow); });
335 assert(pRet);
336 return pRet;
339 void QtInstance::DestroyObject(SalObject* pObject)
341 if (pObject)
343 assert(dynamic_cast<QtObject*>(pObject));
344 Q_EMIT deleteObjectLaterSignal(static_cast<QtObject*>(pObject));
348 std::unique_ptr<SalVirtualDevice>
349 QtInstance::CreateVirtualDevice(SalGraphics& rGraphics, tools::Long& nDX, tools::Long& nDY,
350 DeviceFormat /*eFormat*/, const SystemGraphicsData* pGd)
352 if (m_bUseCairo)
354 SvpSalGraphics* pSvpSalGraphics = dynamic_cast<QtSvpGraphics*>(&rGraphics);
355 assert(pSvpSalGraphics);
356 // tdf#127529 see SvpSalInstance::CreateVirtualDevice for the rare case of a non-null pPreExistingTarget
357 cairo_surface_t* pPreExistingTarget
358 = pGd ? static_cast<cairo_surface_t*>(pGd->pSurface) : nullptr;
359 std::unique_ptr<SalVirtualDevice> pVD(
360 new QtSvpVirtualDevice(pSvpSalGraphics->getSurface(), pPreExistingTarget));
361 pVD->SetSize(nDX, nDY);
362 return pVD;
364 else
366 std::unique_ptr<SalVirtualDevice> pVD(new QtVirtualDevice(/*scale*/ 1));
367 pVD->SetSize(nDX, nDY);
368 return pVD;
372 std::unique_ptr<SalMenu> QtInstance::CreateMenu(bool bMenuBar, Menu* pVCLMenu)
374 SolarMutexGuard aGuard;
375 std::unique_ptr<SalMenu> pRet;
376 RunInMainThread([&pRet, bMenuBar, pVCLMenu]() {
377 QtMenu* pSalMenu = new QtMenu(bMenuBar);
378 pRet.reset(pSalMenu);
379 pSalMenu->SetMenu(pVCLMenu);
381 assert(pRet);
382 return pRet;
385 std::unique_ptr<SalMenuItem> QtInstance::CreateMenuItem(const SalItemParams& rItemData)
387 return std::unique_ptr<SalMenuItem>(new QtMenuItem(&rItemData));
390 SalTimer* QtInstance::CreateSalTimer()
392 m_pTimer = new QtTimer();
393 return m_pTimer;
396 SalSystem* QtInstance::CreateSalSystem() { return new QtSystem; }
398 std::shared_ptr<SalBitmap> QtInstance::CreateSalBitmap()
400 if (m_bUseCairo)
401 return std::make_shared<SvpSalBitmap>();
402 else
403 return std::make_shared<QtBitmap>();
406 bool QtInstance::ImplYield(bool bWait, bool bHandleAllCurrentEvents)
408 // Re-acquire the guard for user events when called via Q_EMIT ImplYieldSignal
409 SolarMutexGuard aGuard;
410 bool wasEvent = DispatchUserEvents(bHandleAllCurrentEvents);
411 if (!bHandleAllCurrentEvents && wasEvent)
412 return true;
415 * Quoting the Qt docs: [QAbstractEventDispatcher::processEvents] processes
416 * pending events that match flags until there are no more events to process.
418 SolarMutexReleaser aReleaser;
419 QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance(qApp->thread());
420 if (bWait && !wasEvent)
421 wasEvent = dispatcher->processEvents(QEventLoop::WaitForMoreEvents);
422 else
423 wasEvent = dispatcher->processEvents(QEventLoop::AllEvents) || wasEvent;
424 return wasEvent;
427 bool QtInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
429 bool bWasEvent = false;
430 if (qApp->thread() == QThread::currentThread())
432 bWasEvent = ImplYield(bWait, bHandleAllCurrentEvents);
433 if (bWasEvent)
434 m_aWaitingYieldCond.set();
436 else
439 SolarMutexReleaser aReleaser;
440 bWasEvent = Q_EMIT ImplYieldSignal(false, bHandleAllCurrentEvents);
442 if (!bWasEvent && bWait)
444 m_aWaitingYieldCond.reset();
445 SolarMutexReleaser aReleaser;
446 m_aWaitingYieldCond.wait();
447 bWasEvent = true;
450 return bWasEvent;
453 bool QtInstance::AnyInput(VclInputFlags nType)
455 bool bResult = false;
456 if (nType & VclInputFlags::TIMER)
457 bResult |= (m_pTimer && m_pTimer->remainingTime() == 0);
458 if (nType & VclInputFlags::OTHER)
459 bResult |= !m_bSleeping;
460 return bResult;
463 OUString QtInstance::GetConnectionIdentifier() { return OUString(); }
465 void QtInstance::AddToRecentDocumentList(const OUString&, const OUString&, const OUString&) {}
467 #ifndef EMSCRIPTEN
468 OpenGLContext* QtInstance::CreateOpenGLContext() { return new QtOpenGLContext; }
469 #endif
471 bool QtInstance::IsMainThread() const
473 return !qApp || (qApp->thread() == QThread::currentThread());
476 void QtInstance::TriggerUserEventProcessing()
478 QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance(qApp->thread());
479 dispatcher->wakeUp();
482 void QtInstance::ProcessEvent(SalUserEvent aEvent)
484 aEvent.m_pFrame->CallCallback(aEvent.m_nEvent, aEvent.m_pData);
487 rtl::Reference<QtFilePicker>
488 QtInstance::createPicker(css::uno::Reference<css::uno::XComponentContext> const& context,
489 QFileDialog::FileMode eMode)
491 if (!IsMainThread())
493 SolarMutexGuard g;
494 rtl::Reference<QtFilePicker> pPicker;
495 RunInMainThread([&, this]() { pPicker = createPicker(context, eMode); });
496 assert(pPicker);
497 return pPicker;
500 return new QtFilePicker(context, eMode);
503 css::uno::Reference<css::ui::dialogs::XFilePicker2>
504 QtInstance::createFilePicker(const css::uno::Reference<css::uno::XComponentContext>& context)
506 return css::uno::Reference<css::ui::dialogs::XFilePicker2>(
507 createPicker(context, QFileDialog::ExistingFile));
510 css::uno::Reference<css::ui::dialogs::XFolderPicker2>
511 QtInstance::createFolderPicker(const css::uno::Reference<css::uno::XComponentContext>& context)
513 return css::uno::Reference<css::ui::dialogs::XFolderPicker2>(
514 createPicker(context, QFileDialog::Directory));
517 css::uno::Reference<css::uno::XInterface>
518 QtInstance::CreateClipboard(const css::uno::Sequence<css::uno::Any>& arguments)
520 OUString sel;
521 if (arguments.getLength() == 0)
523 sel = "CLIPBOARD";
525 else if (arguments.getLength() != 1 || !(arguments[0] >>= sel))
527 throw css::lang::IllegalArgumentException("bad QtInstance::CreateClipboard arguments",
528 css::uno::Reference<css::uno::XInterface>(), -1);
531 // This could also use RunInMain, but SolarMutexGuard is enough
532 // since at this point we're not accessing the clipboard, just get the
533 // accessor to the clipboard.
534 SolarMutexGuard aGuard;
536 auto it = m_aClipboards.find(sel);
537 if (it != m_aClipboards.end())
538 return it->second;
540 css::uno::Reference<css::uno::XInterface> xClipboard = QtClipboard::create(sel);
541 if (xClipboard.is())
542 m_aClipboards[sel] = xClipboard;
544 return xClipboard;
547 css::uno::Reference<css::uno::XInterface>
548 QtInstance::ImplCreateDragSource(const SystemEnvData* pSysEnv)
550 return vcl::X11DnDHelper(new QtDragSource(), pSysEnv->aShellWindow);
553 css::uno::Reference<css::uno::XInterface>
554 QtInstance::ImplCreateDropTarget(const SystemEnvData* pSysEnv)
556 return vcl::X11DnDHelper(new QtDropTarget(), pSysEnv->aShellWindow);
559 IMPL_LINK_NOARG(QtInstance, updateStyleHdl, Timer*, void)
561 SolarMutexGuard aGuard;
562 SalFrame* pFrame = anyFrame();
563 if (pFrame)
565 pFrame->CallCallback(SalEvent::SettingsChanged, nullptr);
566 if (m_bUpdateFonts)
568 pFrame->CallCallback(SalEvent::FontChanged, nullptr);
569 m_bUpdateFonts = false;
574 void QtInstance::UpdateStyle(bool bFontsChanged)
576 if (bFontsChanged)
577 m_bUpdateFonts = true;
578 if (!m_aUpdateStyleTimer.IsActive())
579 m_aUpdateStyleTimer.Start();
582 void* QtInstance::CreateGStreamerSink(const SystemChildWindow* pWindow)
584 // As of 2021-09, qt-gstreamer is unmaintained and there is no Qt 6 video sink
585 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && ENABLE_GSTREAMER_1_0 && QT5_HAVE_GOBJECT
586 auto pSymbol = gstElementFactoryNameSymbol();
587 if (!pSymbol)
588 return nullptr;
590 const SystemEnvData* pEnvData = pWindow->GetSystemData();
591 if (!pEnvData)
592 return nullptr;
594 if (pEnvData->platform != SystemEnvData::Platform::Wayland)
595 return nullptr;
597 GstElement* pVideosink = pSymbol("qwidget5videosink", "qwidget5videosink");
598 if (pVideosink)
600 QWidget* pQWidget = static_cast<QWidget*>(pEnvData->pWidget);
601 g_object_set(G_OBJECT(pVideosink), "widget", pQWidget, nullptr);
603 else
605 SAL_WARN("vcl.qt", "Couldn't initialize qwidget5videosink."
606 " Video playback might not work as expected."
607 " Please install Qt5 packages for QtGStreamer.");
608 // with no videosink explicitly set, GStreamer will open its own (misplaced) window(s) to display video
611 return pVideosink;
612 #else
613 Q_UNUSED(pWindow);
614 return nullptr;
615 #endif
618 void QtInstance::connectQScreenSignals(const QScreen* pScreen)
620 connect(pScreen, &QScreen::orientationChanged, this, &QtInstance::orientationChanged);
621 connect(pScreen, &QScreen::virtualGeometryChanged, this, &QtInstance::virtualGeometryChanged);
624 void QtInstance::notifyDisplayChanged()
626 SolarMutexGuard aGuard;
627 SalFrame* pAnyFrame = anyFrame();
628 if (pAnyFrame)
629 pAnyFrame->CallCallback(SalEvent::DisplayChanged, nullptr);
632 void QtInstance::orientationChanged(Qt::ScreenOrientation) { notifyDisplayChanged(); }
634 void QtInstance::primaryScreenChanged(QScreen*) { notifyDisplayChanged(); }
636 void QtInstance::screenAdded(QScreen* pScreen)
638 connectQScreenSignals(pScreen);
639 if (QApplication::screens().size() == 1)
640 notifyDisplayChanged();
643 void QtInstance::screenRemoved(QScreen*) { notifyDisplayChanged(); }
645 void QtInstance::virtualGeometryChanged(const QRect&) { notifyDisplayChanged(); }
647 void QtInstance::AllocFakeCmdlineArgs(std::unique_ptr<char* []>& rFakeArgv,
648 std::unique_ptr<int>& rFakeArgc,
649 std::vector<FreeableCStr>& rFakeArgvFreeable)
651 OString aVersion(qVersion());
652 SAL_INFO("vcl.qt", "qt version string is " << aVersion);
654 const sal_uInt32 nParams = osl_getCommandArgCount();
655 sal_uInt32 nDisplayValueIdx = 0;
656 OUString aParam, aBin;
658 for (sal_uInt32 nIdx = 0; nIdx < nParams; ++nIdx)
660 osl_getCommandArg(nIdx, &aParam.pData);
661 if (aParam != "-display")
662 continue;
663 ++nIdx;
664 nDisplayValueIdx = nIdx;
667 osl_getExecutableFile(&aParam.pData);
668 osl_getSystemPathFromFileURL(aParam.pData, &aBin.pData);
669 OString aExec = OUStringToOString(aBin, osl_getThreadTextEncoding());
671 std::vector<FreeableCStr> aFakeArgvFreeable;
672 aFakeArgvFreeable.reserve(4);
673 aFakeArgvFreeable.emplace_back(strdup(aExec.getStr()));
674 aFakeArgvFreeable.emplace_back(strdup("--nocrashhandler"));
675 if (nDisplayValueIdx)
677 aFakeArgvFreeable.emplace_back(strdup("-display"));
678 osl_getCommandArg(nDisplayValueIdx, &aParam.pData);
679 OString aDisplay = OUStringToOString(aParam, osl_getThreadTextEncoding());
680 aFakeArgvFreeable.emplace_back(strdup(aDisplay.getStr()));
682 rFakeArgvFreeable.swap(aFakeArgvFreeable);
684 const int nFakeArgc = rFakeArgvFreeable.size();
685 rFakeArgv.reset(new char*[nFakeArgc]);
686 for (int i = 0; i < nFakeArgc; i++)
687 rFakeArgv[i] = rFakeArgvFreeable[i].get();
689 rFakeArgc.reset(new int);
690 *rFakeArgc = nFakeArgc;
693 void QtInstance::MoveFakeCmdlineArgs(std::unique_ptr<char* []>& rFakeArgv,
694 std::unique_ptr<int>& rFakeArgc,
695 std::vector<FreeableCStr>& rFakeArgvFreeable)
697 m_pFakeArgv = std::move(rFakeArgv);
698 m_pFakeArgc = std::move(rFakeArgc);
699 m_pFakeArgvFreeable.swap(rFakeArgvFreeable);
702 std::unique_ptr<QApplication> QtInstance::CreateQApplication(int& nArgc, char** pArgv)
704 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
705 // for Qt 6, setting Qt::AA_EnableHighDpiScaling and Qt::AA_UseHighDpiPixmaps
706 // is deprecated, they're always enabled
707 QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
708 // for scaled icons in the native menus
709 QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
710 #endif
712 FreeableCStr session_manager;
713 if (getenv("SESSION_MANAGER") != nullptr)
715 session_manager.reset(strdup(getenv("SESSION_MANAGER")));
716 unsetenv("SESSION_MANAGER");
719 std::unique_ptr<QApplication> pQApp = std::make_unique<QApplication>(nArgc, pArgv);
721 if (session_manager != nullptr)
723 // coverity[tainted_string] - trusted source for setenv
724 setenv("SESSION_MANAGER", session_manager.get(), 1);
727 QApplication::setQuitOnLastWindowClosed(false);
728 return pQApp;
731 bool QtInstance::DoExecute(int& nExitCode)
733 const bool bIsOnSystemEventLoop = Application::IsOnSystemEventLoop();
734 if (bIsOnSystemEventLoop)
735 nExitCode = QApplication::exec();
736 return bIsOnSystemEventLoop;
739 void QtInstance::DoQuit()
741 if (Application::IsOnSystemEventLoop())
742 QApplication::quit();
745 void QtInstance::setActivePopup(QtFrame* pFrame)
747 assert(!pFrame || pFrame->isPopup());
748 m_pActivePopup = pFrame;
751 extern "C" {
752 VCLPLUG_QT_PUBLIC SalInstance* create_SalInstance()
754 static const bool bUseCairo = (nullptr != getenv("SAL_VCL_QT5_USE_CAIRO"));
756 std::unique_ptr<char* []> pFakeArgv;
757 std::unique_ptr<int> pFakeArgc;
758 std::vector<FreeableCStr> aFakeArgvFreeable;
759 QtInstance::AllocFakeCmdlineArgs(pFakeArgv, pFakeArgc, aFakeArgvFreeable);
761 std::unique_ptr<QApplication> pQApp
762 = QtInstance::CreateQApplication(*pFakeArgc, pFakeArgv.get());
764 QtInstance* pInstance = new QtInstance(pQApp, bUseCairo);
765 pInstance->MoveFakeCmdlineArgs(pFakeArgv, pFakeArgc, aFakeArgvFreeable);
767 new QtData();
769 return pInstance;
773 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */