tdf#143148 Use pragma once instead of include guards
[LibreOffice.git] / vcl / qt5 / QtInstance.cxx
blobaa1c369fbe2dff981d9238a93b727f81cd12ab30
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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 <QtInstanceBuilder.hxx>
32 #include <QtMenu.hxx>
33 #include <QtObject.hxx>
34 #include <QtOpenGLContext.hxx>
35 #include "QtSvpVirtualDevice.hxx"
36 #include <QtSystem.hxx>
37 #include <QtTimer.hxx>
38 #include <QtVirtualDevice.hxx>
39 #include <QtInstanceWidget.hxx>
40 #include <QtInstanceMessageDialog.hxx>
42 #include <headless/svpvd.hxx>
43 #include <salvtables.hxx>
45 #include <QtCore/QAbstractEventDispatcher>
46 #include <QtCore/QLibraryInfo>
47 #include <QtCore/QThread>
48 #include <QtGui/QScreen>
49 #include <QtWidgets/QApplication>
50 #include <QtWidgets/QWidget>
51 #include <QtWidgets/QMessageBox>
53 #include <vclpluginapi.h>
54 #include <tools/debug.hxx>
55 #include <comphelper/flagguard.hxx>
56 #include <config_emscripten.h>
57 #include <config_vclplug.h>
58 #include <dndhelper.hxx>
59 #include <vcl/qt/QtUtils.hxx>
60 #include <vcl/stdtext.hxx>
61 #include <vcl/sysdata.hxx>
62 #include <sal/log.hxx>
63 #include <o3tl/unreachable.hxx>
64 #include <osl/process.h>
65 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && ENABLE_GSTREAMER_1_0 && QT5_HAVE_GOBJECT
66 #include <unx/gstsink.hxx>
67 #endif
68 #include <headless/svpbmp.hxx>
70 #include <mutex>
71 #include <condition_variable>
73 #ifdef EMSCRIPTEN
74 #include <QtCore/QtPlugin>
75 Q_IMPORT_PLUGIN(QWasmIntegrationPlugin)
76 #if defined DISABLE_DYNLOADING && ENABLE_QT6
77 #include <QtCore/QtResource>
78 #endif
79 #endif
81 namespace
83 /// TODO: not much Qt specific here? could be generalised, esp. for OSX...
84 /// this subclass allows for the transfer of a closure for running on the main
85 /// thread, to handle all the thread affine stuff in Qt; the SolarMutex is
86 /// "loaned" to the main thread for the execution of the closure.
87 /// @note it doesn't work to just use "emit" and signals/slots to move calls to
88 /// the main thread, because the other thread has the SolarMutex; the other
89 /// thread (typically) cannot release SolarMutex, because then the main thread
90 /// will handle all sorts of events and whatnot; this design ensures that the
91 /// main thread only runs the passed closure (unless the closure releases
92 /// SolarMutex itself, which should probably be avoided).
93 class QtYieldMutex : public SalYieldMutex
95 public:
96 /// flag only accessed on main thread:
97 /// main thread has "borrowed" SolarMutex from another thread
98 bool m_bNoYieldLock = false;
99 /// members for communication from non-main thread to main thread
100 std::mutex m_RunInMainMutex;
101 std::condition_variable m_InMainCondition;
102 bool m_isWakeUpMain = false;
103 std::function<void()> m_Closure; ///< code for main thread to run
104 /// members for communication from main thread to non-main thread
105 std::condition_variable m_ResultCondition;
106 bool m_isResultReady = false;
108 virtual bool IsCurrentThread() const override;
109 virtual void doAcquire(sal_uInt32 nLockCount) override;
110 virtual sal_uInt32 doRelease(bool const bUnlockAll) override;
113 void lcl_setStandardButtons(QtInstanceMessageDialog& rMessageDialog, VclButtonsType eButtonType)
115 switch (eButtonType)
117 case VclButtonsType::NONE:
118 break;
119 case VclButtonsType::Ok:
120 rMessageDialog.add_button(GetStandardText(StandardButtonType::OK), RET_OK);
121 break;
122 case VclButtonsType::Close:
123 rMessageDialog.add_button(GetStandardText(StandardButtonType::Close), RET_CLOSE);
124 break;
125 case VclButtonsType::Cancel:
126 rMessageDialog.add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL);
127 break;
128 case VclButtonsType::YesNo:
129 rMessageDialog.add_button(GetStandardText(StandardButtonType::Yes), RET_YES);
130 rMessageDialog.add_button(GetStandardText(StandardButtonType::No), RET_NO);
131 break;
132 case VclButtonsType::OkCancel:
133 rMessageDialog.add_button(GetStandardText(StandardButtonType::OK), RET_OK);
134 rMessageDialog.add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL);
135 break;
136 default:
137 assert(false && "Unhandled VCLButtonsType");
142 bool QtYieldMutex::IsCurrentThread() const
144 QtInstance& rQtInstance = GetQtInstance();
145 if (rQtInstance.IsMainThread() && m_bNoYieldLock)
147 return true; // main thread has borrowed SolarMutex
149 return SalYieldMutex::IsCurrentThread();
152 void QtYieldMutex::doAcquire(sal_uInt32 nLockCount)
154 QtInstance& rQtInstance = GetQtInstance();
155 if (!rQtInstance.IsMainThread())
157 SalYieldMutex::doAcquire(nLockCount);
158 return;
160 if (m_bNoYieldLock)
162 return; // special case for main thread: borrowed from other thread
164 do // main thread acquire...
166 std::function<void()> func; // copy of closure on thread stack
168 std::unique_lock<std::mutex> g(m_RunInMainMutex);
169 if (m_aMutex.tryToAcquire())
171 // if there's a closure, the other thread holds m_aMutex
172 assert(!m_Closure);
173 m_isWakeUpMain = false;
174 --nLockCount; // have acquired once!
175 ++m_nCount;
176 break;
178 m_InMainCondition.wait(g, [this]() { return m_isWakeUpMain; });
179 m_isWakeUpMain = false;
180 std::swap(func, m_Closure);
182 if (func)
184 assert(!m_bNoYieldLock);
185 m_bNoYieldLock = true; // execute closure with borrowed SolarMutex
186 func();
187 m_bNoYieldLock = false;
188 std::scoped_lock<std::mutex> g(m_RunInMainMutex);
189 assert(!m_isResultReady);
190 m_isResultReady = true;
191 m_ResultCondition.notify_all(); // unblock other thread
193 } while (true);
194 SalYieldMutex::doAcquire(nLockCount);
197 sal_uInt32 QtYieldMutex::doRelease(bool const bUnlockAll)
199 QtInstance& rQtInstance = GetQtInstance();
200 if (rQtInstance.IsMainThread() && m_bNoYieldLock)
202 return 1; // dummy value
205 std::scoped_lock<std::mutex> g(m_RunInMainMutex);
206 // read m_nCount before doRelease (it's guarded by m_aMutex)
207 bool const isReleased(bUnlockAll || m_nCount == 1);
208 sal_uInt32 nCount = SalYieldMutex::doRelease(bUnlockAll);
209 if (isReleased && !rQtInstance.IsMainThread())
211 m_isWakeUpMain = true;
212 m_InMainCondition.notify_all(); // unblock main thread
214 return nCount;
217 // this could be abstracted to be independent of Qt by passing in the
218 // event-trigger as another function parameter...
219 // it could also be a template of the return type, then it could return the
220 // result of func... but then how to handle the result in doAcquire?
221 void QtInstance::RunInMainThread(std::function<void()> func)
223 DBG_TESTSOLARMUTEX();
224 if (IsMainThread())
226 func();
227 return;
230 QtYieldMutex* const pMutex(static_cast<QtYieldMutex*>(GetYieldMutex()));
232 std::scoped_lock<std::mutex> g(pMutex->m_RunInMainMutex);
233 assert(!pMutex->m_Closure);
234 pMutex->m_Closure = func;
235 // unblock main thread in case it is blocked on condition
236 pMutex->m_isWakeUpMain = true;
237 pMutex->m_InMainCondition.notify_all();
240 TriggerUserEventProcessing();
242 std::unique_lock<std::mutex> g(pMutex->m_RunInMainMutex);
243 pMutex->m_ResultCondition.wait(g, [pMutex]() { return pMutex->m_isResultReady; });
244 pMutex->m_isResultReady = false;
248 OUString QtInstance::constructToolkitID(std::u16string_view sTKname)
250 OUString sID(sTKname + OUString::Concat(u" ("));
251 if (m_bUseCairo)
252 sID += "cairo+";
253 else
254 sID += "qfont+";
255 sID += toOUString(QGuiApplication::platformName()) + OUString::Concat(u")");
256 return sID;
259 QtInstance::QtInstance(std::unique_ptr<QApplication>& pQApp)
260 : SalGenericInstance(std::make_unique<QtYieldMutex>())
261 , m_bUseCairo(nullptr == getenv("SAL_VCL_QT_USE_QFONT"))
262 , m_pTimer(nullptr)
263 , m_bSleeping(false)
264 , m_pQApplication(std::move(pQApp))
265 , m_aUpdateStyleTimer("vcl::qt5 m_aUpdateStyleTimer")
266 , m_bUpdateFonts(false)
267 , m_pActivePopup(nullptr)
269 ImplSVData* pSVData = ImplGetSVData();
270 const OUString sToolkit = "qt" + OUString::number(QT_VERSION_MAJOR);
271 pSVData->maAppData.mxToolkitName = constructToolkitID(sToolkit);
273 // this one needs to be blocking, so that the handling in main thread
274 // is processed before the thread emitting the signal continues
275 connect(this, &QtInstance::ImplYieldSignal, this, &QtInstance::ImplYield,
276 Qt::BlockingQueuedConnection);
278 // this one needs to be queued non-blocking
279 // in order to have this event arriving to correct event processing loop
280 connect(this, &QtInstance::deleteObjectLaterSignal, this,
281 [](QObject* pObject) { QtInstance::deleteObjectLater(pObject); }, Qt::QueuedConnection);
283 m_aUpdateStyleTimer.SetTimeout(50);
284 m_aUpdateStyleTimer.SetInvokeHandler(LINK(this, QtInstance, updateStyleHdl));
286 QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance(qApp->thread());
287 connect(dispatcher, &QAbstractEventDispatcher::awake, this, [this]() { m_bSleeping = false; });
288 connect(dispatcher, &QAbstractEventDispatcher::aboutToBlock, this,
289 [this]() { m_bSleeping = true; });
291 connect(QGuiApplication::inputMethod(), &QInputMethod::localeChanged, this,
292 &QtInstance::localeChanged);
294 for (const QScreen* pCurScreen : QApplication::screens())
295 connectQScreenSignals(pCurScreen);
296 connect(qApp, &QGuiApplication::primaryScreenChanged, this, &QtInstance::primaryScreenChanged);
297 connect(qApp, &QGuiApplication::screenAdded, this, &QtInstance::screenAdded);
298 connect(qApp, &QGuiApplication::screenRemoved, this, &QtInstance::screenRemoved);
300 #ifndef EMSCRIPTEN
301 m_bSupportsOpenGL = true;
302 #elif !HAVE_EMSCRIPTEN_JSPI
303 ImplGetSVData()->maAppData.m_bUseSystemLoop = true;
304 #endif
307 QtInstance::~QtInstance()
309 // force freeing the QApplication before freeing the arguments,
310 // as it uses references to the provided arguments!
311 m_pQApplication.reset();
314 void QtInstance::AfterAppInit()
316 // set the default application icon via desktop file just on Wayland,
317 // as this otherwise overrides the individual desktop icons on X11.
318 if (QGuiApplication::platformName() == "wayland")
319 QGuiApplication::setDesktopFileName(QStringLiteral("libreoffice-startcenter"));
320 QGuiApplication::setLayoutDirection(AllSettings::GetLayoutRTL() ? Qt::RightToLeft
321 : Qt::LeftToRight);
324 void QtInstance::localeChanged()
326 SolarMutexGuard aGuard;
327 const vcl::Window* pFocusWindow = Application::GetFocusWindow();
328 SalFrame* const pFocusFrame = pFocusWindow ? pFocusWindow->ImplGetFrame() : nullptr;
329 if (!pFocusFrame)
330 return;
332 const LanguageTag aTag(
333 toOUString(QGuiApplication::inputMethod()->locale().name().replace("_", "-")));
334 static_cast<QtFrame*>(pFocusFrame)->setInputLanguage(aTag.getLanguageType());
337 void QtInstance::deleteObjectLater(QObject* pObject) { pObject->deleteLater(); }
339 SalFrame* QtInstance::CreateChildFrame(SystemParentData* /*pParent*/, SalFrameStyleFlags nStyle)
341 SolarMutexGuard aGuard;
342 SalFrame* pRet(nullptr);
343 RunInMainThread([&, this]() { pRet = new QtFrame(nullptr, nStyle, useCairo()); });
344 assert(pRet);
345 return pRet;
348 SalFrame* QtInstance::CreateFrame(SalFrame* pParent, SalFrameStyleFlags nStyle)
350 SolarMutexGuard aGuard;
352 assert(!pParent || dynamic_cast<QtFrame*>(pParent));
354 SalFrame* pRet(nullptr);
355 RunInMainThread(
356 [&, this]() { pRet = new QtFrame(static_cast<QtFrame*>(pParent), nStyle, useCairo()); });
357 assert(pRet);
358 return pRet;
361 void QtInstance::DestroyFrame(SalFrame* pFrame)
363 if (pFrame)
365 assert(dynamic_cast<QtFrame*>(pFrame));
366 Q_EMIT deleteObjectLaterSignal(static_cast<QtFrame*>(pFrame));
370 SalObject* QtInstance::CreateObject(SalFrame* pParent, SystemWindowData*, bool bShow)
372 SolarMutexGuard aGuard;
374 assert(!pParent || dynamic_cast<QtFrame*>(pParent));
376 SalObject* pRet(nullptr);
377 RunInMainThread([&]() { pRet = new QtObject(static_cast<QtFrame*>(pParent), bShow); });
378 assert(pRet);
379 return pRet;
382 void QtInstance::DestroyObject(SalObject* pObject)
384 if (pObject)
386 assert(dynamic_cast<QtObject*>(pObject));
387 Q_EMIT deleteObjectLaterSignal(static_cast<QtObject*>(pObject));
391 std::unique_ptr<SalVirtualDevice>
392 QtInstance::CreateVirtualDevice(SalGraphics& rGraphics, tools::Long& nDX, tools::Long& nDY,
393 DeviceFormat /*eFormat*/, const SystemGraphicsData* pGd)
395 if (m_bUseCairo)
397 SvpSalGraphics* pSvpSalGraphics = dynamic_cast<QtSvpGraphics*>(&rGraphics);
398 assert(pSvpSalGraphics);
399 // tdf#127529 see SvpSalInstance::CreateVirtualDevice for the rare case of a non-null pPreExistingTarget
400 cairo_surface_t* pPreExistingTarget
401 = pGd ? static_cast<cairo_surface_t*>(pGd->pSurface) : nullptr;
402 std::unique_ptr<SalVirtualDevice> pVD(
403 new QtSvpVirtualDevice(pSvpSalGraphics->getSurface(), pPreExistingTarget));
404 pVD->SetSize(nDX, nDY);
405 return pVD;
407 else
409 std::unique_ptr<SalVirtualDevice> pVD(new QtVirtualDevice(/*scale*/ 1));
410 pVD->SetSize(nDX, nDY);
411 return pVD;
415 std::unique_ptr<SalMenu> QtInstance::CreateMenu(bool bMenuBar, Menu* pVCLMenu)
417 SolarMutexGuard aGuard;
418 std::unique_ptr<SalMenu> pRet;
419 RunInMainThread([&pRet, bMenuBar, pVCLMenu]() {
420 QtMenu* pSalMenu = new QtMenu(bMenuBar);
421 pRet.reset(pSalMenu);
422 pSalMenu->SetMenu(pVCLMenu);
424 assert(pRet);
425 return pRet;
428 std::unique_ptr<SalMenuItem> QtInstance::CreateMenuItem(const SalItemParams& rItemData)
430 return std::unique_ptr<SalMenuItem>(new QtMenuItem(&rItemData));
433 SalTimer* QtInstance::CreateSalTimer()
435 m_pTimer = new QtTimer();
436 return m_pTimer;
439 SalSystem* QtInstance::CreateSalSystem() { return new QtSystem; }
441 std::shared_ptr<SalBitmap> QtInstance::CreateSalBitmap()
443 if (m_bUseCairo)
444 return std::make_shared<SvpSalBitmap>();
445 else
446 return std::make_shared<QtBitmap>();
449 bool QtInstance::ImplYield(bool bWait, bool bHandleAllCurrentEvents)
451 // Re-acquire the guard for user events when called via Q_EMIT ImplYieldSignal
452 SolarMutexGuard aGuard;
453 bool wasEvent = DispatchUserEvents(bHandleAllCurrentEvents);
454 if (!bHandleAllCurrentEvents && wasEvent)
455 return true;
458 * Quoting the Qt docs: [QAbstractEventDispatcher::processEvents] processes
459 * pending events that match flags until there are no more events to process.
461 SolarMutexReleaser aReleaser;
462 QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance(qApp->thread());
463 if (bWait && !wasEvent)
464 wasEvent = dispatcher->processEvents(QEventLoop::WaitForMoreEvents);
465 else
466 wasEvent = dispatcher->processEvents(QEventLoop::AllEvents) || wasEvent;
467 return wasEvent;
470 bool QtInstance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
472 bool bWasEvent = false;
473 if (qApp->thread() == QThread::currentThread())
475 bWasEvent = ImplYield(bWait, bHandleAllCurrentEvents);
476 if (bWasEvent)
477 m_aWaitingYieldCond.set();
479 else
482 SolarMutexReleaser aReleaser;
483 bWasEvent = Q_EMIT ImplYieldSignal(false, bHandleAllCurrentEvents);
485 if (!bWasEvent && bWait)
487 m_aWaitingYieldCond.reset();
488 SolarMutexReleaser aReleaser;
489 m_aWaitingYieldCond.wait();
490 bWasEvent = true;
493 return bWasEvent;
496 bool QtInstance::AnyInput(VclInputFlags nType)
498 bool bResult = false;
499 if (nType & VclInputFlags::TIMER)
500 bResult |= (m_pTimer && m_pTimer->remainingTime() == 0);
501 if (nType & VclInputFlags::OTHER)
502 bResult |= !m_bSleeping;
503 return bResult;
506 OUString QtInstance::GetConnectionIdentifier() { return OUString(); }
508 void QtInstance::AddToRecentDocumentList(const OUString&, const OUString&, const OUString&) {}
510 #ifndef EMSCRIPTEN
511 OpenGLContext* QtInstance::CreateOpenGLContext() { return new QtOpenGLContext; }
512 #endif
514 bool QtInstance::IsMainThread() const
516 return !qApp || (qApp->thread() == QThread::currentThread());
519 void QtInstance::TriggerUserEventProcessing()
521 QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance(qApp->thread());
522 dispatcher->wakeUp();
525 void QtInstance::ProcessEvent(SalUserEvent aEvent)
527 aEvent.m_pFrame->CallCallback(aEvent.m_nEvent, aEvent.m_pData);
530 rtl::Reference<QtFilePicker>
531 QtInstance::createPicker(css::uno::Reference<css::uno::XComponentContext> const& context,
532 QFileDialog::FileMode eMode)
534 if (!IsMainThread())
536 SolarMutexGuard g;
537 rtl::Reference<QtFilePicker> pPicker;
538 RunInMainThread([&, this]() { pPicker = createPicker(context, eMode); });
539 assert(pPicker);
540 return pPicker;
543 return new QtFilePicker(context, eMode);
546 css::uno::Reference<css::ui::dialogs::XFilePicker2>
547 QtInstance::createFilePicker(const css::uno::Reference<css::uno::XComponentContext>& context)
549 return css::uno::Reference<css::ui::dialogs::XFilePicker2>(
550 createPicker(context, QFileDialog::ExistingFile));
553 css::uno::Reference<css::ui::dialogs::XFolderPicker2>
554 QtInstance::createFolderPicker(const css::uno::Reference<css::uno::XComponentContext>& context)
556 return css::uno::Reference<css::ui::dialogs::XFolderPicker2>(
557 createPicker(context, QFileDialog::Directory));
560 css::uno::Reference<css::uno::XInterface>
561 QtInstance::CreateClipboard(const css::uno::Sequence<css::uno::Any>& arguments)
563 OUString sel;
564 if (arguments.getLength() == 0)
566 sel = "CLIPBOARD";
568 else if (arguments.getLength() != 1 || !(arguments[0] >>= sel))
570 throw css::lang::IllegalArgumentException(u"bad QtInstance::CreateClipboard arguments"_ustr,
571 css::uno::Reference<css::uno::XInterface>(), -1);
574 // This could also use RunInMain, but SolarMutexGuard is enough
575 // since at this point we're not accessing the clipboard, just get the
576 // accessor to the clipboard.
577 SolarMutexGuard aGuard;
579 auto it = m_aClipboards.find(sel);
580 if (it != m_aClipboards.end())
581 return it->second;
583 css::uno::Reference<css::uno::XInterface> xClipboard = QtClipboard::create(sel);
584 if (xClipboard.is())
585 m_aClipboards[sel] = xClipboard;
587 return xClipboard;
590 css::uno::Reference<css::uno::XInterface>
591 QtInstance::ImplCreateDragSource(const SystemEnvData* pSysEnv)
593 return vcl::X11DnDHelper(new QtDragSource(), pSysEnv->aShellWindow);
596 css::uno::Reference<css::uno::XInterface>
597 QtInstance::ImplCreateDropTarget(const SystemEnvData* pSysEnv)
599 return vcl::X11DnDHelper(new QtDropTarget(), pSysEnv->aShellWindow);
602 IMPL_LINK_NOARG(QtInstance, updateStyleHdl, Timer*, void)
604 SolarMutexGuard aGuard;
605 SalFrame* pFrame = anyFrame();
606 if (pFrame)
608 pFrame->CallCallback(SalEvent::SettingsChanged, nullptr);
609 if (m_bUpdateFonts)
611 pFrame->CallCallback(SalEvent::FontChanged, nullptr);
612 m_bUpdateFonts = false;
617 void QtInstance::UpdateStyle(bool bFontsChanged)
619 if (bFontsChanged)
620 m_bUpdateFonts = true;
621 if (!m_aUpdateStyleTimer.IsActive())
622 m_aUpdateStyleTimer.Start();
625 void* QtInstance::CreateGStreamerSink(const SystemChildWindow* pWindow)
627 // As of 2021-09, qt-gstreamer is unmaintained and there is no Qt 6 video sink
628 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && ENABLE_GSTREAMER_1_0 && QT5_HAVE_GOBJECT
629 auto pSymbol = gstElementFactoryNameSymbol();
630 if (!pSymbol)
631 return nullptr;
633 const SystemEnvData* pEnvData = pWindow->GetSystemData();
634 if (!pEnvData)
635 return nullptr;
637 if (pEnvData->platform != SystemEnvData::Platform::Wayland)
638 return nullptr;
640 GstElement* pVideosink = pSymbol("qwidget5videosink", "qwidget5videosink");
641 if (pVideosink)
643 QWidget* pQWidget = static_cast<QWidget*>(pEnvData->pWidget);
644 g_object_set(G_OBJECT(pVideosink), "widget", pQWidget, nullptr);
646 else
648 SAL_WARN("vcl.qt", "Couldn't initialize qwidget5videosink."
649 " Video playback might not work as expected."
650 " Please install Qt5 packages for QtGStreamer.");
651 // with no videosink explicitly set, GStreamer will open its own (misplaced) window(s) to display video
654 return pVideosink;
655 #else
656 Q_UNUSED(pWindow);
657 return nullptr;
658 #endif
661 void QtInstance::connectQScreenSignals(const QScreen* pScreen)
663 connect(pScreen, &QScreen::orientationChanged, this, &QtInstance::orientationChanged);
664 connect(pScreen, &QScreen::virtualGeometryChanged, this, &QtInstance::virtualGeometryChanged);
667 void QtInstance::notifyDisplayChanged()
669 SolarMutexGuard aGuard;
670 SalFrame* pAnyFrame = anyFrame();
671 if (pAnyFrame)
672 pAnyFrame->CallCallback(SalEvent::DisplayChanged, nullptr);
675 void QtInstance::orientationChanged(Qt::ScreenOrientation) { notifyDisplayChanged(); }
677 void QtInstance::primaryScreenChanged(QScreen*) { notifyDisplayChanged(); }
679 void QtInstance::screenAdded(QScreen* pScreen)
681 connectQScreenSignals(pScreen);
682 if (QApplication::screens().size() == 1)
683 notifyDisplayChanged();
686 void QtInstance::screenRemoved(QScreen*) { notifyDisplayChanged(); }
688 void QtInstance::virtualGeometryChanged(const QRect&) { notifyDisplayChanged(); }
690 void QtInstance::AllocFakeCmdlineArgs(std::unique_ptr<char* []>& rFakeArgv,
691 std::unique_ptr<int>& rFakeArgc,
692 std::vector<FreeableCStr>& rFakeArgvFreeable)
694 OString aVersion(qVersion());
695 SAL_INFO("vcl.qt", "qt version string is " << aVersion);
697 const sal_uInt32 nParams = osl_getCommandArgCount();
698 sal_uInt32 nDisplayValueIdx = 0;
699 OUString aParam, aBin;
701 for (sal_uInt32 nIdx = 0; nIdx < nParams; ++nIdx)
703 osl_getCommandArg(nIdx, &aParam.pData);
704 if (aParam != "-display")
705 continue;
706 ++nIdx;
707 nDisplayValueIdx = nIdx;
710 osl_getExecutableFile(&aParam.pData);
711 osl_getSystemPathFromFileURL(aParam.pData, &aBin.pData);
712 OString aExec = OUStringToOString(aBin, osl_getThreadTextEncoding());
714 std::vector<FreeableCStr> aFakeArgvFreeable;
715 aFakeArgvFreeable.reserve(4);
716 aFakeArgvFreeable.emplace_back(strdup(aExec.getStr()));
717 aFakeArgvFreeable.emplace_back(strdup("--nocrashhandler"));
718 if (nDisplayValueIdx)
720 aFakeArgvFreeable.emplace_back(strdup("-display"));
721 osl_getCommandArg(nDisplayValueIdx, &aParam.pData);
722 OString aDisplay = OUStringToOString(aParam, osl_getThreadTextEncoding());
723 aFakeArgvFreeable.emplace_back(strdup(aDisplay.getStr()));
725 rFakeArgvFreeable.swap(aFakeArgvFreeable);
727 const int nFakeArgc = rFakeArgvFreeable.size();
728 rFakeArgv.reset(new char*[nFakeArgc]);
729 for (int i = 0; i < nFakeArgc; i++)
730 rFakeArgv[i] = rFakeArgvFreeable[i].get();
732 rFakeArgc.reset(new int);
733 *rFakeArgc = nFakeArgc;
736 void QtInstance::MoveFakeCmdlineArgs(std::unique_ptr<char* []>& rFakeArgv,
737 std::unique_ptr<int>& rFakeArgc,
738 std::vector<FreeableCStr>& rFakeArgvFreeable)
740 m_pFakeArgv = std::move(rFakeArgv);
741 m_pFakeArgc = std::move(rFakeArgc);
742 m_pFakeArgvFreeable.swap(rFakeArgvFreeable);
745 std::unique_ptr<QApplication> QtInstance::CreateQApplication(int& nArgc, char** pArgv)
747 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
748 // for Qt 6, setting Qt::AA_EnableHighDpiScaling and Qt::AA_UseHighDpiPixmaps
749 // is deprecated, they're always enabled
750 QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
751 // for scaled icons in the native menus
752 QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
753 #endif
754 // force Qt::HighDpiScaleFactorRoundingPolicy::Round, which is the Qt 5 default
755 // policy and prevents incorrect rendering with the Qt 6 default policy
756 // Qt::HighDpiScaleFactorRoundingPolicy::PassThrough (tdf#159915)
757 QGuiApplication::setHighDpiScaleFactorRoundingPolicy(
758 Qt::HighDpiScaleFactorRoundingPolicy::Round);
760 FreeableCStr session_manager;
761 if (getenv("SESSION_MANAGER") != nullptr)
763 session_manager.reset(strdup(getenv("SESSION_MANAGER")));
764 unsetenv("SESSION_MANAGER");
767 std::unique_ptr<QApplication> pQApp = std::make_unique<QApplication>(nArgc, pArgv);
769 if (session_manager != nullptr)
771 // coverity[tainted_string] - trusted source for setenv
772 setenv("SESSION_MANAGER", session_manager.get(), 1);
775 QApplication::setQuitOnLastWindowClosed(false);
776 return pQApp;
779 bool QtInstance::DoExecute(int& nExitCode)
781 const bool bIsUseSystemEventLoop = Application::IsUseSystemEventLoop();
782 if (bIsUseSystemEventLoop)
784 #if defined EMSCRIPTEN
785 // For Emscripten, QApplication::exec() will unwind the stack by throwing a JavaScript
786 // exception, so we need to manually undo the call of AcquireYieldMutex() done in InitVCL:
787 ReleaseYieldMutex(false);
788 #endif
789 nExitCode = QApplication::exec();
790 #if defined EMSCRIPTEN
791 O3TL_UNREACHABLE;
792 #endif
794 return bIsUseSystemEventLoop;
797 void QtInstance::DoQuit()
799 if (Application::IsUseSystemEventLoop())
800 QApplication::quit();
803 void QtInstance::setActivePopup(QtFrame* pFrame)
805 assert(!pFrame || pFrame->isPopup());
806 m_pActivePopup = pFrame;
809 QWidget* QtInstance::GetNativeParentFromWeldParent(weld::Widget* pParent)
811 if (!pParent)
812 return nullptr;
814 if (QtInstanceWidget* pQtInstanceWidget = dynamic_cast<QtInstanceWidget*>(pParent))
815 return pQtInstanceWidget->getQWidget();
817 // the parent is not welded/not a native Qt widget; get QWidget via frame
818 if (SalInstanceWidget* pSalWidget = dynamic_cast<SalInstanceWidget*>(pParent))
820 if (vcl::Window* pWindow = pSalWidget->getWidget())
822 if (QtFrame* pFrame = static_cast<QtFrame*>(pWindow->ImplGetFrame()))
823 return pFrame->GetQWidget();
827 return nullptr;
830 std::unique_ptr<weld::Builder>
831 QtInstance::CreateBuilder(weld::Widget* pParent, const OUString& rUIRoot, const OUString& rUIFile)
833 // for now, require explicitly enabling use of QtInstanceBuilder via SAL_VCL_QT_USE_WELDED_WIDGETS
834 static const bool bUseWeldedWidgets = (getenv("SAL_VCL_QT_USE_WELDED_WIDGETS") != nullptr);
835 if (bUseWeldedWidgets && !QtData::noWeldedWidgets()
836 && QtInstanceBuilder::IsUIFileSupported(rUIFile))
838 QWidget* pQtParent = GetNativeParentFromWeldParent(pParent);
839 return std::make_unique<QtInstanceBuilder>(pQtParent, rUIRoot, rUIFile);
841 else
843 return SalInstance::CreateBuilder(pParent, rUIRoot, rUIFile);
847 weld::MessageDialog* QtInstance::CreateMessageDialog(weld::Widget* pParent,
848 VclMessageType eMessageType,
849 VclButtonsType eButtonsType,
850 const OUString& rPrimaryMessage)
852 SolarMutexGuard g;
853 if (!IsMainThread())
855 weld::MessageDialog* pDialog;
856 RunInMainThread([&] {
857 pDialog = CreateMessageDialog(pParent, eMessageType, eButtonsType, rPrimaryMessage);
859 return pDialog;
862 if (QtData::noWeldedWidgets())
864 return SalInstance::CreateMessageDialog(pParent, eMessageType, eButtonsType,
865 rPrimaryMessage);
867 else
869 QWidget* pQtParent = GetNativeParentFromWeldParent(pParent);
870 QMessageBox* pMessageBox = new QMessageBox(pQtParent);
871 pMessageBox->setText(toQString(rPrimaryMessage));
872 pMessageBox->setIcon(vclMessageTypeToQtIcon(eMessageType));
873 pMessageBox->setWindowTitle(vclMessageTypeToQtTitle(eMessageType));
874 QtInstanceMessageDialog* pDialog = new QtInstanceMessageDialog(pMessageBox);
875 lcl_setStandardButtons(*pDialog, eButtonsType);
876 return pDialog;
880 static void initResources()
882 #if defined EMSCRIPTEN && defined DISABLE_DYNLOADING && ENABLE_QT6
883 // Make sure the resources from Qt6's plugins/platforms/libqwasm.a are not stripped out of a
884 // statically linked binary (and this code cannot be directly in extern "C" create_SalInstance,
885 // as the expansion of Q_INIT_RESOURCE contains extern function declarations that would then
886 // erroneously be C function declarations):
887 Q_INIT_RESOURCE(wasmfonts);
888 Q_INIT_RESOURCE(wasmwindow);
889 #endif
892 extern "C" {
893 VCLPLUG_QT_PUBLIC SalInstance* create_SalInstance()
895 initResources();
897 std::unique_ptr<char* []> pFakeArgv;
898 std::unique_ptr<int> pFakeArgc;
899 std::vector<FreeableCStr> aFakeArgvFreeable;
900 QtInstance::AllocFakeCmdlineArgs(pFakeArgv, pFakeArgc, aFakeArgvFreeable);
902 std::unique_ptr<QApplication> pQApp
903 = QtInstance::CreateQApplication(*pFakeArgc, pFakeArgv.get());
905 QtInstance* pInstance = new QtInstance(pQApp);
906 pInstance->MoveFakeCmdlineArgs(pFakeArgv, pFakeArgc, aFakeArgvFreeable);
908 new QtData();
910 return pInstance;
914 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */