nss: upgrade to release 3.73
[LibreOffice.git] / vcl / qt5 / Qt5Instance.cxx
blobbceae35e9016873fac43800ea806fbccc854da70
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 <Qt5Instance.hxx>
21 #include <Qt5Instance.moc>
23 #include <com/sun/star/lang/IllegalArgumentException.hpp>
25 #include <Qt5Bitmap.hxx>
26 #include <Qt5Clipboard.hxx>
27 #include <Qt5Data.hxx>
28 #include <Qt5DragAndDrop.hxx>
29 #include <Qt5FilePicker.hxx>
30 #include <Qt5Frame.hxx>
31 #include <Qt5Menu.hxx>
32 #include <Qt5Object.hxx>
33 #include <Qt5OpenGLContext.hxx>
34 #include "Qt5SvpVirtualDevice.hxx"
35 #include <Qt5System.hxx>
36 #include <Qt5Timer.hxx>
37 #include <Qt5VirtualDevice.hxx>
39 #include <headless/svpvd.hxx>
41 #include <QtCore/QAbstractEventDispatcher>
42 #include <QtCore/QThread>
43 #include <QtWidgets/QApplication>
44 #include <QtWidgets/QWidget>
46 #include <vclpluginapi.h>
47 #include <tools/debug.hxx>
48 #include <comphelper/flagguard.hxx>
49 #include <sal/log.hxx>
50 #include <osl/process.h>
51 #include <unx/gstsink.hxx>
52 #include <headless/svpbmp.hxx>
54 #include <mutex>
55 #include <condition_variable>
57 namespace
59 /// TODO: not much Qt5 specific here? could be generalised, esp. for OSX...
60 /// this subclass allows for the transfer of a closure for running on the main
61 /// thread, to handle all the thread affine stuff in Qt5; the SolarMutex is
62 /// "loaned" to the main thread for the execution of the closure.
63 /// @note it doesn't work to just use "emit" and signals/slots to move calls to
64 /// the main thread, because the other thread has the SolarMutex; the other
65 /// thread (typically) cannot release SolarMutex, because then the main thread
66 /// will handle all sorts of events and whatnot; this design ensures that the
67 /// main thread only runs the passed closure (unless the closure releases
68 /// SolarMutex itself, which should probably be avoided).
69 class Qt5YieldMutex : public SalYieldMutex
71 public:
72 /// flag only accessed on main thread:
73 /// main thread has "borrowed" SolarMutex from another thread
74 bool m_bNoYieldLock = false;
75 /// members for communication from non-main thread to main thread
76 std::mutex m_RunInMainMutex;
77 std::condition_variable m_InMainCondition;
78 bool m_isWakeUpMain = false;
79 std::function<void()> m_Closure; ///< code for main thread to run
80 /// members for communication from main thread to non-main thread
81 std::condition_variable m_ResultCondition;
82 bool m_isResultReady = false;
84 virtual bool IsCurrentThread() const override;
85 virtual void doAcquire(sal_uInt32 nLockCount) override;
86 virtual sal_uInt32 doRelease(bool const bUnlockAll) override;
90 bool Qt5YieldMutex::IsCurrentThread() const
92 auto const* pSalInst(static_cast<Qt5Instance const*>(GetSalData()->m_pInstance));
93 assert(pSalInst);
94 if (pSalInst->IsMainThread() && m_bNoYieldLock)
96 return true; // main thread has borrowed SolarMutex
98 return SalYieldMutex::IsCurrentThread();
101 void Qt5YieldMutex::doAcquire(sal_uInt32 nLockCount)
103 auto const* pSalInst(static_cast<Qt5Instance const*>(GetSalData()->m_pInstance));
104 assert(pSalInst);
105 if (!pSalInst->IsMainThread())
107 SalYieldMutex::doAcquire(nLockCount);
108 return;
110 if (m_bNoYieldLock)
112 return; // special case for main thread: borrowed from other thread
114 do // main thread acquire...
116 std::function<void()> func; // copy of closure on thread stack
118 std::unique_lock<std::mutex> g(m_RunInMainMutex);
119 if (m_aMutex.tryToAcquire())
121 // if there's a closure, the other thread holds m_aMutex
122 assert(!m_Closure);
123 m_isWakeUpMain = false;
124 --nLockCount; // have acquired once!
125 ++m_nCount;
126 break;
128 m_InMainCondition.wait(g, [this]() { return m_isWakeUpMain; });
129 m_isWakeUpMain = false;
130 std::swap(func, m_Closure);
132 if (func)
134 assert(!m_bNoYieldLock);
135 m_bNoYieldLock = true; // execute closure with borrowed SolarMutex
136 func();
137 m_bNoYieldLock = false;
138 std::scoped_lock<std::mutex> g(m_RunInMainMutex);
139 assert(!m_isResultReady);
140 m_isResultReady = true;
141 m_ResultCondition.notify_all(); // unblock other thread
143 } while (true);
144 SalYieldMutex::doAcquire(nLockCount);
147 sal_uInt32 Qt5YieldMutex::doRelease(bool const bUnlockAll)
149 auto const* pSalInst(static_cast<Qt5Instance const*>(GetSalData()->m_pInstance));
150 assert(pSalInst);
151 if (pSalInst->IsMainThread() && m_bNoYieldLock)
153 return 1; // dummy value
156 std::scoped_lock<std::mutex> g(m_RunInMainMutex);
157 // read m_nCount before doRelease (it's guarded by m_aMutex)
158 bool const isReleased(bUnlockAll || m_nCount == 1);
159 sal_uInt32 nCount = SalYieldMutex::doRelease(bUnlockAll);
160 if (isReleased && !pSalInst->IsMainThread())
162 m_isWakeUpMain = true;
163 m_InMainCondition.notify_all(); // unblock main thread
165 return nCount;
168 // this could be abstracted to be independent of Qt5 by passing in the
169 // event-trigger as another function parameter...
170 // it could also be a template of the return type, then it could return the
171 // result of func... but then how to handle the result in doAcquire?
172 void Qt5Instance::RunInMainThread(std::function<void()> func)
174 DBG_TESTSOLARMUTEX();
175 if (IsMainThread())
177 func();
178 return;
181 Qt5YieldMutex* const pMutex(static_cast<Qt5YieldMutex*>(GetYieldMutex()));
183 std::scoped_lock<std::mutex> g(pMutex->m_RunInMainMutex);
184 assert(!pMutex->m_Closure);
185 pMutex->m_Closure = func;
186 // unblock main thread in case it is blocked on condition
187 pMutex->m_isWakeUpMain = true;
188 pMutex->m_InMainCondition.notify_all();
191 TriggerUserEventProcessing();
193 std::unique_lock<std::mutex> g(pMutex->m_RunInMainMutex);
194 pMutex->m_ResultCondition.wait(g, [pMutex]() { return pMutex->m_isResultReady; });
195 pMutex->m_isResultReady = false;
199 Qt5Instance::Qt5Instance(std::unique_ptr<QApplication>& pQApp, bool bUseCairo)
200 : SalGenericInstance(std::make_unique<Qt5YieldMutex>())
201 , m_bUseCairo(bUseCairo)
202 , m_pTimer(nullptr)
203 , m_bSleeping(false)
204 , m_pQApplication(std::move(pQApp))
205 , m_aUpdateStyleTimer("vcl::qt5 m_aUpdateStyleTimer")
206 , m_bUpdateFonts(false)
208 ImplSVData* pSVData = ImplGetSVData();
209 if (bUseCairo)
210 pSVData->maAppData.mxToolkitName = OUString("qt5+cairo");
211 else
212 pSVData->maAppData.mxToolkitName = OUString("qt5");
214 // this one needs to be blocking, so that the handling in main thread
215 // is processed before the thread emitting the signal continues
216 connect(this, SIGNAL(ImplYieldSignal(bool, bool)), this, SLOT(ImplYield(bool, bool)),
217 Qt::BlockingQueuedConnection);
219 // this one needs to be queued non-blocking
220 // in order to have this event arriving to correct event processing loop
221 connect(this, &Qt5Instance::deleteObjectLaterSignal, this,
222 [](QObject* pObject) { Qt5Instance::deleteObjectLater(pObject); },
223 Qt::QueuedConnection);
225 m_aUpdateStyleTimer.SetTimeout(50);
226 m_aUpdateStyleTimer.SetInvokeHandler(LINK(this, Qt5Instance, updateStyleHdl));
228 QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance(qApp->thread());
229 connect(dispatcher, &QAbstractEventDispatcher::awake, this, [this]() { m_bSleeping = false; });
230 connect(dispatcher, &QAbstractEventDispatcher::aboutToBlock, this,
231 [this]() { m_bSleeping = true; });
234 Qt5Instance::~Qt5Instance()
236 // force freeing the QApplication before freeing the arguments,
237 // as it uses references to the provided arguments!
238 m_pQApplication.reset();
241 void Qt5Instance::AfterAppInit()
243 // set the default application icon via desktop file just on Wayland,
244 // as this otherwise overrides the individual desktop icons on X11.
245 if (QGuiApplication::platformName() == "wayland")
246 QGuiApplication::setDesktopFileName(QStringLiteral("libreoffice-startcenter.desktop"));
247 QGuiApplication::setLayoutDirection(AllSettings::GetLayoutRTL() ? Qt::RightToLeft
248 : Qt::LeftToRight);
251 void Qt5Instance::deleteObjectLater(QObject* pObject) { pObject->deleteLater(); }
253 SalFrame* Qt5Instance::CreateChildFrame(SystemParentData* /*pParent*/, SalFrameStyleFlags nStyle)
255 SalFrame* pRet(nullptr);
256 RunInMainThread([&, this]() { pRet = new Qt5Frame(nullptr, nStyle, useCairo()); });
257 assert(pRet);
258 return pRet;
261 SalFrame* Qt5Instance::CreateFrame(SalFrame* pParent, SalFrameStyleFlags nStyle)
263 assert(!pParent || dynamic_cast<Qt5Frame*>(pParent));
265 SalFrame* pRet(nullptr);
266 RunInMainThread(
267 [&, this]() { pRet = new Qt5Frame(static_cast<Qt5Frame*>(pParent), nStyle, useCairo()); });
268 assert(pRet);
269 return pRet;
272 void Qt5Instance::DestroyFrame(SalFrame* pFrame)
274 if (pFrame)
276 assert(dynamic_cast<Qt5Frame*>(pFrame));
277 Q_EMIT deleteObjectLaterSignal(static_cast<Qt5Frame*>(pFrame));
281 SalObject* Qt5Instance::CreateObject(SalFrame* pParent, SystemWindowData*, bool bShow)
283 assert(!pParent || dynamic_cast<Qt5Frame*>(pParent));
285 SalObject* pRet(nullptr);
286 RunInMainThread([&]() { pRet = new Qt5Object(static_cast<Qt5Frame*>(pParent), bShow); });
287 assert(pRet);
288 return pRet;
291 void Qt5Instance::DestroyObject(SalObject* pObject)
293 if (pObject)
295 assert(dynamic_cast<Qt5Object*>(pObject));
296 Q_EMIT deleteObjectLaterSignal(static_cast<Qt5Object*>(pObject));
300 std::unique_ptr<SalVirtualDevice>
301 Qt5Instance::CreateVirtualDevice(SalGraphics* pGraphics, tools::Long& nDX, tools::Long& nDY,
302 DeviceFormat eFormat, const SystemGraphicsData* pGd)
304 if (m_bUseCairo)
306 SvpSalGraphics* pSvpSalGraphics = dynamic_cast<Qt5SvpGraphics*>(pGraphics);
307 assert(pSvpSalGraphics);
308 // tdf#127529 see SvpSalInstance::CreateVirtualDevice for the rare case of a non-null pPreExistingTarget
309 cairo_surface_t* pPreExistingTarget
310 = pGd ? static_cast<cairo_surface_t*>(pGd->pSurface) : nullptr;
311 std::unique_ptr<SalVirtualDevice> pVD(
312 new Qt5SvpVirtualDevice(eFormat, pSvpSalGraphics->getSurface(), pPreExistingTarget));
313 pVD->SetSize(nDX, nDY);
314 return pVD;
316 else
318 std::unique_ptr<SalVirtualDevice> pVD(new Qt5VirtualDevice(eFormat, 1));
319 pVD->SetSize(nDX, nDY);
320 return pVD;
324 std::unique_ptr<SalMenu> Qt5Instance::CreateMenu(bool bMenuBar, Menu* pVCLMenu)
326 std::unique_ptr<SalMenu> pRet;
327 RunInMainThread([&pRet, bMenuBar, pVCLMenu]() {
328 Qt5Menu* pSalMenu = new Qt5Menu(bMenuBar);
329 pRet.reset(pSalMenu);
330 pSalMenu->SetMenu(pVCLMenu);
332 assert(pRet);
333 return pRet;
336 std::unique_ptr<SalMenuItem> Qt5Instance::CreateMenuItem(const SalItemParams& rItemData)
338 return std::unique_ptr<SalMenuItem>(new Qt5MenuItem(&rItemData));
341 SalTimer* Qt5Instance::CreateSalTimer()
343 m_pTimer = new Qt5Timer();
344 return m_pTimer;
347 SalSystem* Qt5Instance::CreateSalSystem() { return new Qt5System; }
349 std::shared_ptr<SalBitmap> Qt5Instance::CreateSalBitmap()
351 if (m_bUseCairo)
352 return std::make_shared<SvpSalBitmap>();
353 else
354 return std::make_shared<Qt5Bitmap>();
357 bool Qt5Instance::ImplYield(bool bWait, bool bHandleAllCurrentEvents)
359 // Re-acquire the guard for user events when called via Q_EMIT ImplYieldSignal
360 SolarMutexGuard aGuard;
361 bool wasEvent = DispatchUserEvents(bHandleAllCurrentEvents);
362 if (!bHandleAllCurrentEvents && wasEvent)
363 return true;
366 * Quoting the Qt docs: [QAbstractEventDispatcher::processEvents] processes
367 * pending events that match flags until there are no more events to process.
369 SolarMutexReleaser aReleaser;
370 QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance(qApp->thread());
371 if (bWait && !wasEvent)
372 wasEvent = dispatcher->processEvents(QEventLoop::WaitForMoreEvents);
373 else
374 wasEvent = dispatcher->processEvents(QEventLoop::AllEvents) || wasEvent;
375 return wasEvent;
378 bool Qt5Instance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
380 bool bWasEvent = false;
381 if (qApp->thread() == QThread::currentThread())
383 bWasEvent = ImplYield(bWait, bHandleAllCurrentEvents);
384 if (bWasEvent)
385 m_aWaitingYieldCond.set();
387 else
390 SolarMutexReleaser aReleaser;
391 bWasEvent = Q_EMIT ImplYieldSignal(false, bHandleAllCurrentEvents);
393 if (!bWasEvent && bWait)
395 m_aWaitingYieldCond.reset();
396 SolarMutexReleaser aReleaser;
397 m_aWaitingYieldCond.wait();
398 bWasEvent = true;
401 return bWasEvent;
404 bool Qt5Instance::AnyInput(VclInputFlags nType)
406 bool bResult = false;
407 if (nType & VclInputFlags::TIMER)
408 bResult |= (m_pTimer && m_pTimer->remainingTime() == 0);
409 if (nType & VclInputFlags::OTHER)
410 bResult |= !m_bSleeping;
411 return bResult;
414 OUString Qt5Instance::GetConnectionIdentifier() { return OUString(); }
416 void Qt5Instance::AddToRecentDocumentList(const OUString&, const OUString&, const OUString&) {}
418 OpenGLContext* Qt5Instance::CreateOpenGLContext() { return new Qt5OpenGLContext; }
420 bool Qt5Instance::IsMainThread() const
422 return !qApp || (qApp->thread() == QThread::currentThread());
425 void Qt5Instance::TriggerUserEventProcessing()
427 QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance(qApp->thread());
428 dispatcher->wakeUp();
431 void Qt5Instance::ProcessEvent(SalUserEvent aEvent)
433 aEvent.m_pFrame->CallCallback(aEvent.m_nEvent, aEvent.m_pData);
436 Qt5FilePicker*
437 Qt5Instance::createPicker(css::uno::Reference<css::uno::XComponentContext> const& context,
438 QFileDialog::FileMode eMode)
440 if (!IsMainThread())
442 SolarMutexGuard g;
443 Qt5FilePicker* pPicker;
444 RunInMainThread([&, this]() { pPicker = createPicker(context, eMode); });
445 assert(pPicker);
446 return pPicker;
449 return new Qt5FilePicker(context, eMode);
452 css::uno::Reference<css::ui::dialogs::XFilePicker2>
453 Qt5Instance::createFilePicker(const css::uno::Reference<css::uno::XComponentContext>& context)
455 return css::uno::Reference<css::ui::dialogs::XFilePicker2>(
456 createPicker(context, QFileDialog::ExistingFile));
459 css::uno::Reference<css::ui::dialogs::XFolderPicker2>
460 Qt5Instance::createFolderPicker(const css::uno::Reference<css::uno::XComponentContext>& context)
462 return css::uno::Reference<css::ui::dialogs::XFolderPicker2>(
463 createPicker(context, QFileDialog::Directory));
466 css::uno::Reference<css::uno::XInterface>
467 Qt5Instance::CreateClipboard(const css::uno::Sequence<css::uno::Any>& arguments)
469 OUString sel;
470 if (arguments.getLength() == 0)
472 sel = "CLIPBOARD";
474 else if (arguments.getLength() != 1 || !(arguments[0] >>= sel))
476 throw css::lang::IllegalArgumentException("bad Qt5Instance::CreateClipboard arguments",
477 css::uno::Reference<css::uno::XInterface>(), -1);
480 // This could also use RunInMain, but SolarMutexGuard is enough
481 // since at this point we're not accessing the clipboard, just get the
482 // accessor to the clipboard.
483 SolarMutexGuard aGuard;
485 auto it = m_aClipboards.find(sel);
486 if (it != m_aClipboards.end())
487 return it->second;
489 css::uno::Reference<css::uno::XInterface> xClipboard = Qt5Clipboard::create(sel);
490 if (xClipboard.is())
491 m_aClipboards[sel] = xClipboard;
493 return xClipboard;
496 css::uno::Reference<css::uno::XInterface> Qt5Instance::CreateDragSource()
498 return css::uno::Reference<css::uno::XInterface>(
499 static_cast<cppu::OWeakObject*>(new Qt5DragSource()));
502 css::uno::Reference<css::uno::XInterface> Qt5Instance::CreateDropTarget()
504 return css::uno::Reference<css::uno::XInterface>(
505 static_cast<cppu::OWeakObject*>(new Qt5DropTarget()));
508 IMPL_LINK_NOARG(Qt5Instance, updateStyleHdl, Timer*, void)
510 SolarMutexGuard aGuard;
511 SalFrame* pFrame = anyFrame();
512 if (pFrame)
514 pFrame->CallCallback(SalEvent::SettingsChanged, nullptr);
515 if (m_bUpdateFonts)
517 pFrame->CallCallback(SalEvent::FontChanged, nullptr);
518 m_bUpdateFonts = false;
523 void Qt5Instance::UpdateStyle(bool bFontsChanged)
525 if (bFontsChanged)
526 m_bUpdateFonts = true;
527 if (!m_aUpdateStyleTimer.IsActive())
528 m_aUpdateStyleTimer.Start();
531 void* Qt5Instance::CreateGStreamerSink(const SystemChildWindow* pWindow)
533 #if ENABLE_GSTREAMER_1_0 && QT5_HAVE_GOBJECT
534 auto pSymbol = gstElementFactoryNameSymbol();
535 if (!pSymbol)
536 return nullptr;
538 const SystemEnvData* pEnvData = pWindow->GetSystemData();
539 if (!pEnvData)
540 return nullptr;
542 if (pEnvData->platform != SystemEnvData::Platform::Wayland)
543 return nullptr;
545 GstElement* pVideosink = pSymbol("qwidget5videosink", "qwidget5videosink");
546 if (pVideosink)
548 QWidget* pQWidget = static_cast<QWidget*>(pEnvData->pWidget);
549 g_object_set(G_OBJECT(pVideosink), "widget", pQWidget, nullptr);
551 else
553 SAL_WARN("vcl.qt5", "Couldn't initialize qwidget5videosink."
554 " Video playback might not work as expected."
555 " Please install Qt5 packages for QtGStreamer.");
556 // with no videosink explicitly set, GStreamer will open its own (misplaced) window(s) to display video
559 return pVideosink;
560 #else
561 (void*)pWindow;
562 return nullptr;
563 #endif
566 void Qt5Instance::AllocFakeCmdlineArgs(std::unique_ptr<char* []>& rFakeArgv,
567 std::unique_ptr<int>& rFakeArgc,
568 std::vector<FreeableCStr>& rFakeArgvFreeable)
570 OString aVersion(qVersion());
571 SAL_INFO("vcl.qt5", "qt version string is " << aVersion);
573 const sal_uInt32 nParams = osl_getCommandArgCount();
574 sal_uInt32 nDisplayValueIdx = 0;
575 OUString aParam, aBin;
577 for (sal_uInt32 nIdx = 0; nIdx < nParams; ++nIdx)
579 osl_getCommandArg(nIdx, &aParam.pData);
580 if (aParam != "-display")
581 continue;
582 ++nIdx;
583 nDisplayValueIdx = nIdx;
586 osl_getExecutableFile(&aParam.pData);
587 osl_getSystemPathFromFileURL(aParam.pData, &aBin.pData);
588 OString aExec = OUStringToOString(aBin, osl_getThreadTextEncoding());
590 std::vector<FreeableCStr> aFakeArgvFreeable;
591 aFakeArgvFreeable.reserve(4);
592 aFakeArgvFreeable.emplace_back(strdup(aExec.getStr()));
593 aFakeArgvFreeable.emplace_back(strdup("--nocrashhandler"));
594 if (nDisplayValueIdx)
596 aFakeArgvFreeable.emplace_back(strdup("-display"));
597 osl_getCommandArg(nDisplayValueIdx, &aParam.pData);
598 OString aDisplay = OUStringToOString(aParam, osl_getThreadTextEncoding());
599 aFakeArgvFreeable.emplace_back(strdup(aDisplay.getStr()));
601 rFakeArgvFreeable.swap(aFakeArgvFreeable);
603 const int nFakeArgc = rFakeArgvFreeable.size();
604 rFakeArgv.reset(new char*[nFakeArgc]);
605 for (int i = 0; i < nFakeArgc; i++)
606 rFakeArgv[i] = rFakeArgvFreeable[i].get();
608 rFakeArgc.reset(new int);
609 *rFakeArgc = nFakeArgc;
612 void Qt5Instance::MoveFakeCmdlineArgs(std::unique_ptr<char* []>& rFakeArgv,
613 std::unique_ptr<int>& rFakeArgc,
614 std::vector<FreeableCStr>& rFakeArgvFreeable)
616 m_pFakeArgv = std::move(rFakeArgv);
617 m_pFakeArgc = std::move(rFakeArgc);
618 m_pFakeArgvFreeable.swap(rFakeArgvFreeable);
621 std::unique_ptr<QApplication> Qt5Instance::CreateQApplication(int& nArgc, char** pArgv)
623 QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
624 // for scaled icons in the native menus
625 QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
627 FreeableCStr session_manager;
628 if (getenv("SESSION_MANAGER") != nullptr)
630 session_manager.reset(strdup(getenv("SESSION_MANAGER")));
631 unsetenv("SESSION_MANAGER");
634 std::unique_ptr<QApplication> pQApp = std::make_unique<QApplication>(nArgc, pArgv);
636 if (session_manager != nullptr)
638 // coverity[tainted_string] - trusted source for setenv
639 setenv("SESSION_MANAGER", session_manager.get(), 1);
642 QApplication::setQuitOnLastWindowClosed(false);
643 return pQApp;
646 extern "C" {
647 VCLPLUG_QT5_PUBLIC SalInstance* create_SalInstance()
649 static const bool bUseCairo = (nullptr != getenv("SAL_VCL_QT5_USE_CAIRO"));
651 std::unique_ptr<char* []> pFakeArgv;
652 std::unique_ptr<int> pFakeArgc;
653 std::vector<FreeableCStr> aFakeArgvFreeable;
654 Qt5Instance::AllocFakeCmdlineArgs(pFakeArgv, pFakeArgc, aFakeArgvFreeable);
656 std::unique_ptr<QApplication> pQApp
657 = Qt5Instance::CreateQApplication(*pFakeArgc, pFakeArgv.get());
659 Qt5Instance* pInstance = new Qt5Instance(pQApp, bUseCairo);
660 pInstance->MoveFakeCmdlineArgs(pFakeArgv, pFakeArgc, aFakeArgvFreeable);
662 new Qt5Data(pInstance);
664 return pInstance;
668 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */