1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
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>
31 #include <QtInstanceBuilder.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>
68 #include <headless/svpbmp.hxx>
71 #include <condition_variable>
74 #include <QtCore/QtPlugin>
75 Q_IMPORT_PLUGIN(QWasmIntegrationPlugin
)
76 #if defined DISABLE_DYNLOADING && ENABLE_QT6
77 #include <QtCore/QtResource>
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
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
)
117 case VclButtonsType::NONE
:
119 case VclButtonsType::Ok
:
120 rMessageDialog
.add_button(GetStandardText(StandardButtonType::OK
), RET_OK
);
122 case VclButtonsType::Close
:
123 rMessageDialog
.add_button(GetStandardText(StandardButtonType::Close
), RET_CLOSE
);
125 case VclButtonsType::Cancel
:
126 rMessageDialog
.add_button(GetStandardText(StandardButtonType::Cancel
), RET_CANCEL
);
128 case VclButtonsType::YesNo
:
129 rMessageDialog
.add_button(GetStandardText(StandardButtonType::Yes
), RET_YES
);
130 rMessageDialog
.add_button(GetStandardText(StandardButtonType::No
), RET_NO
);
132 case VclButtonsType::OkCancel
:
133 rMessageDialog
.add_button(GetStandardText(StandardButtonType::OK
), RET_OK
);
134 rMessageDialog
.add_button(GetStandardText(StandardButtonType::Cancel
), RET_CANCEL
);
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
);
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
173 m_isWakeUpMain
= false;
174 --nLockCount
; // have acquired once!
178 m_InMainCondition
.wait(g
, [this]() { return m_isWakeUpMain
; });
179 m_isWakeUpMain
= false;
180 std::swap(func
, m_Closure
);
184 assert(!m_bNoYieldLock
);
185 m_bNoYieldLock
= true; // execute closure with borrowed SolarMutex
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
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
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();
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
" ("));
255 sID
+= toOUString(QGuiApplication::platformName()) + OUString::Concat(u
")");
259 QtInstance::QtInstance(std::unique_ptr
<QApplication
>& pQApp
)
260 : SalGenericInstance(std::make_unique
<QtYieldMutex
>())
261 , m_bUseCairo(nullptr == getenv("SAL_VCL_QT_USE_QFONT"))
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
);
301 m_bSupportsOpenGL
= true;
302 #elif !HAVE_EMSCRIPTEN_JSPI
303 ImplGetSVData()->maAppData
.m_bUseSystemLoop
= true;
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
324 void QtInstance::localeChanged()
326 SolarMutexGuard aGuard
;
327 const vcl::Window
* pFocusWindow
= Application::GetFocusWindow();
328 SalFrame
* const pFocusFrame
= pFocusWindow
? pFocusWindow
->ImplGetFrame() : nullptr;
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()); });
348 SalFrame
* QtInstance::CreateFrame(SalFrame
* pParent
, SalFrameStyleFlags nStyle
)
350 SolarMutexGuard aGuard
;
352 assert(!pParent
|| dynamic_cast<QtFrame
*>(pParent
));
354 SalFrame
* pRet(nullptr);
356 [&, this]() { pRet
= new QtFrame(static_cast<QtFrame
*>(pParent
), nStyle
, useCairo()); });
361 void QtInstance::DestroyFrame(SalFrame
* 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
); });
382 void QtInstance::DestroyObject(SalObject
* 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
)
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
);
409 std::unique_ptr
<SalVirtualDevice
> pVD(new QtVirtualDevice(/*scale*/ 1));
410 pVD
->SetSize(nDX
, nDY
);
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
);
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();
439 SalSystem
* QtInstance::CreateSalSystem() { return new QtSystem
; }
441 std::shared_ptr
<SalBitmap
> QtInstance::CreateSalBitmap()
444 return std::make_shared
<SvpSalBitmap
>();
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
)
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
);
466 wasEvent
= dispatcher
->processEvents(QEventLoop::AllEvents
) || wasEvent
;
470 bool QtInstance::DoYield(bool bWait
, bool bHandleAllCurrentEvents
)
472 bool bWasEvent
= false;
473 if (qApp
->thread() == QThread::currentThread())
475 bWasEvent
= ImplYield(bWait
, bHandleAllCurrentEvents
);
477 m_aWaitingYieldCond
.set();
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();
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
;
506 OUString
QtInstance::GetConnectionIdentifier() { return OUString(); }
508 void QtInstance::AddToRecentDocumentList(const OUString
&, const OUString
&, const OUString
&) {}
511 OpenGLContext
* QtInstance::CreateOpenGLContext() { return new QtOpenGLContext
; }
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
)
537 rtl::Reference
<QtFilePicker
> pPicker
;
538 RunInMainThread([&, this]() { pPicker
= createPicker(context
, eMode
); });
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
)
564 if (arguments
.getLength() == 0)
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())
583 css::uno::Reference
<css::uno::XInterface
> xClipboard
= QtClipboard::create(sel
);
585 m_aClipboards
[sel
] = 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();
608 pFrame
->CallCallback(SalEvent::SettingsChanged
, nullptr);
611 pFrame
->CallCallback(SalEvent::FontChanged
, nullptr);
612 m_bUpdateFonts
= false;
617 void QtInstance::UpdateStyle(bool 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();
633 const SystemEnvData
* pEnvData
= pWindow
->GetSystemData();
637 if (pEnvData
->platform
!= SystemEnvData::Platform::Wayland
)
640 GstElement
* pVideosink
= pSymbol("qwidget5videosink", "qwidget5videosink");
643 QWidget
* pQWidget
= static_cast<QWidget
*>(pEnvData
->pWidget
);
644 g_object_set(G_OBJECT(pVideosink
), "widget", pQWidget
, nullptr);
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
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();
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")
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
);
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);
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);
789 nExitCode
= QApplication::exec();
790 #if defined EMSCRIPTEN
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
)
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();
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
);
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
)
855 weld::MessageDialog
* pDialog
;
856 RunInMainThread([&] {
857 pDialog
= CreateMessageDialog(pParent
, eMessageType
, eButtonsType
, rPrimaryMessage
);
862 if (QtData::noWeldedWidgets())
864 return SalInstance::CreateMessageDialog(pParent
, eMessageType
, eButtonsType
,
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
);
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
);
893 VCLPLUG_QT_PUBLIC SalInstance
* create_SalInstance()
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
);
914 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */