/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
namespace
{ /// TODO: not much Qt specific here? could be generalised, esp. for OSX... /// this subclass allows for the transfer of a closure for running on the main /// thread, to handle all the thread affine stuff in Qt; the SolarMutex is /// "loaned" to the main thread for the execution of the closure. /// @note it doesn't work to just use "emit" and signals/slots to move calls to /// the main thread, because the other thread has the SolarMutex; the other /// thread (typically) cannot release SolarMutex, because then the main thread /// will handle all sorts of events and whatnot; this design ensures that the /// main thread only runs the passed closure (unless the closure releases /// SolarMutex itself, which should probably be avoided). class QtYieldMutex : public SalYieldMutex
{ public: /// flag only accessed on main thread: /// main thread has "borrowed" SolarMutex from another thread bool m_bNoYieldLock = false; /// members for communication from non-main thread to main thread
std::mutex m_RunInMainMutex;
std::condition_variable m_InMainCondition; bool m_isWakeUpMain = false;
std::function<void()> m_Closure; ///< code for main thread to run /// members for communication from main thread to non-main thread
std::condition_variable m_ResultCondition; bool m_isResultReady = false;
bool QtYieldMutex::IsCurrentThread() const
{
QtInstance& rQtInstance = GetQtInstance(); if (rQtInstance.IsMainThread() && m_bNoYieldLock)
{ returntrue; // main thread has borrowed SolarMutex
} return SalYieldMutex::IsCurrentThread();
}
void QtYieldMutex::doAcquire(sal_uInt32 nLockCount)
{
QtInstance& rQtInstance = GetQtInstance(); if (!rQtInstance.IsMainThread())
{
SalYieldMutex::doAcquire(nLockCount); return;
} if (m_bNoYieldLock)
{ return; // special case for main thread: borrowed from other thread
} do// main thread acquire...
{
std::function<void()> func; // copy of closure on thread stack
{
std::unique_lock<std::mutex> g(m_RunInMainMutex); if (m_aMutex.tryToAcquire())
{ // if there's a closure, the other thread holds m_aMutex
assert(!m_Closure);
m_isWakeUpMain = false;
--nLockCount; // have acquired once!
++m_nCount; break;
}
m_InMainCondition.wait(g, [this]() { return m_isWakeUpMain; });
m_isWakeUpMain = false;
std::swap(func, m_Closure);
} if (func)
{
assert(!m_bNoYieldLock);
m_bNoYieldLock = true; // execute closure with borrowed SolarMutex
func();
m_bNoYieldLock = false;
std::scoped_lock<std::mutex> g(m_RunInMainMutex);
assert(!m_isResultReady);
m_isResultReady = true;
m_ResultCondition.notify_all(); // unblock other thread
}
} while (true);
SalYieldMutex::doAcquire(nLockCount);
}
sal_uInt32 QtYieldMutex::doRelease(boolconst bUnlockAll)
{
QtInstance& rQtInstance = GetQtInstance(); if (rQtInstance.IsMainThread() && m_bNoYieldLock)
{ return 1; // dummy value
}
std::scoped_lock<std::mutex> g(m_RunInMainMutex); // read m_nCount before doRelease (it's guarded by m_aMutex) boolconst isReleased(bUnlockAll || m_nCount == 1);
sal_uInt32 nCount = SalYieldMutex::doRelease(bUnlockAll); if (isReleased && !rQtInstance.IsMainThread())
{
m_isWakeUpMain = true;
m_InMainCondition.notify_all(); // unblock main thread
} return nCount;
}
// this could be abstracted to be independent of Qt by passing in the // event-trigger as another function parameter... // it could also be a template of the return type, then it could return the // result of func... but then how to handle the result in doAcquire? void QtInstance::RunInMainThread(std::function<void()> func)
{
DBG_TESTSOLARMUTEX(); if (IsMainThread())
{
func(); return;
} #ifdefined EMSCRIPTEN && ENABLE_QT6 && HAVE_EMSCRIPTEN_JSPI && !HAVE_EMSCRIPTEN_PROXY_TO_PTHREAD if (pthread_self() == m_emscriptenThreadingData->eventHandlerThread)
{
EmscriptenLightweightRunInMainThread(func); return;
} #endif
QtYieldMutex* const pMutex(static_cast<QtYieldMutex*>(GetYieldMutex()));
{
std::scoped_lock<std::mutex> g(pMutex->m_RunInMainMutex);
assert(!pMutex->m_Closure);
pMutex->m_Closure = func; // unblock main thread in case it is blocked on condition
pMutex->m_isWakeUpMain = true;
pMutex->m_InMainCondition.notify_all();
}
// this one needs to be blocking, so that the handling in main thread // is processed before the thread emitting the signal continues
connect(this, &QtInstance::ImplYieldSignal, this, &QtInstance::ImplYield,
Qt::BlockingQueuedConnection);
// this one needs to be queued non-blocking // in order to have this event arriving to correct event processing loop
connect(this, &QtInstance::deleteObjectLaterSignal, this,
[](QObject* pObject) { QtInstance::deleteObjectLater(pObject); }, Qt::QueuedConnection);
QtInstance::~QtInstance()
{ // force freeing the QApplication before freeing the arguments, // as it uses references to the provided arguments!
m_pQApplication.reset();
void QtInstance::AfterAppInit()
{ // set the default application icon via desktop file just on Wayland, // as this otherwise overrides the individual desktop icons on X11. if (QGuiApplication::platformName() == "wayland")
QGuiApplication::setDesktopFileName(QStringLiteral("libreoffice-startcenter"));
QGuiApplication::setLayoutDirection(AllSettings::GetLayoutRTL() ? Qt::RightToLeft
: Qt::LeftToRight);
}
bool QtInstance::ImplYield(bool bWait, bool bHandleAllCurrentEvents)
{ // Re-acquire the guard for user events when called via Q_EMIT ImplYieldSignal
SolarMutexGuard aGuard; bool wasEvent = DispatchUserEvents(bHandleAllCurrentEvents); if (!bHandleAllCurrentEvents && wasEvent) returntrue;
/** * Quoting the Qt docs: [QAbstractEventDispatcher::processEvents] processes * pending events that match flags until there are no more events to process.
*/
SolarMutexReleaser aReleaser;
QAbstractEventDispatcher* dispatcher = QAbstractEventDispatcher::instance(qApp->thread()); if (bWait && !wasEvent)
wasEvent = dispatcher->processEvents(QEventLoop::WaitForMoreEvents); else
wasEvent = dispatcher->processEvents(QEventLoop::AllEvents) || wasEvent; return wasEvent;
}
// This could also use RunInMain, but SolarMutexGuard is enough // since at this point we're not accessing the clipboard, just get the // accessor to the clipboard.
SolarMutexGuard aGuard;
auto it = m_aClipboards.find(sel); if (it != m_aClipboards.end()) return it->second;
void QtInstance::UpdateStyle(bool bFontsChanged)
{ if (bFontsChanged)
m_bUpdateFonts = true; if (!m_aUpdateStyleTimer.IsActive())
m_aUpdateStyleTimer.Start();
}
void* QtInstance::CreateGStreamerSink(const SystemChildWindow* pWindow)
{ // As of 2021-09, qt-gstreamer is unmaintained and there is no Qt 6 video sink #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) && ENABLE_GSTREAMER_1_0 && QT5_HAVE_GOBJECT auto pSymbol = gstElementFactoryNameSymbol(); if (!pSymbol) return nullptr;
const SystemEnvData* pEnvData = pWindow->GetSystemData(); if (!pEnvData) return nullptr;
if (pEnvData->platform != SystemEnvData::Platform::Wayland) return nullptr;
GstElement* pVideosink = pSymbol("qwidget5videosink", "qwidget5videosink"); if (pVideosink)
{
QWidget* pQWidget = static_cast<QWidget*>(pEnvData->pWidget);
g_object_set(G_OBJECT(pVideosink), "widget", pQWidget, nullptr);
} else
{
SAL_WARN("vcl.qt", "Couldn't initialize qwidget5videosink." " Video playback might not work as expected." " Please install Qt5 packages for QtGStreamer."); // with no videosink explicitly set, GStreamer will open its own (misplaced) window(s) to display video
}
std::unique_ptr<QApplication> QtInstance::CreateQApplication(int& nArgc, char** pArgv)
{ #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // for Qt 6, setting Qt::AA_EnableHighDpiScaling and Qt::AA_UseHighDpiPixmaps // is deprecated, they're always enabled
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); // for scaled icons in the native menus
QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); #endif // force Qt::HighDpiScaleFactorRoundingPolicy::Round, which is the Qt 5 default // policy and prevents incorrect rendering with the Qt 6 default policy // Qt::HighDpiScaleFactorRoundingPolicy::PassThrough (tdf#159915)
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(
Qt::HighDpiScaleFactorRoundingPolicy::Round);
FreeableCStr session_manager; if (getenv("SESSION_MANAGER") != nullptr)
{
session_manager.reset(strdup(getenv("SESSION_MANAGER")));
unsetenv("SESSION_MANAGER");
}
bool QtInstance::DoExecute(int& nExitCode)
{ constbool bIsUseSystemEventLoop = Application::IsUseSystemEventLoop(); if (bIsUseSystemEventLoop)
{ #ifdefined EMSCRIPTEN // For Emscripten, QApplication::exec() will unwind the stack by throwing a JavaScript // exception, so we need to manually undo the call of AcquireYieldMutex() done in InitVCL:
ReleaseYieldMutex(false); #endif
nExitCode = QApplication::exec(); #ifdefined EMSCRIPTEN
O3TL_UNREACHABLE; #endif
} return bIsUseSystemEventLoop;
}
void QtInstance::DoQuit()
{ if (Application::IsUseSystemEventLoop())
QApplication::quit();
}
QWidget* QtInstance::GetNativeParentFromWeldParent(weld::Widget* pParent)
{ if (!pParent) return nullptr;
if (QtInstanceWidget* pQtInstanceWidget = dynamic_cast<QtInstanceWidget*>(pParent)) return pQtInstanceWidget->getQWidget();
// the parent is not welded/not a native Qt widget; get QWidget via frame if (SalInstanceWidget* pSalWidget = dynamic_cast<SalInstanceWidget*>(pParent))
{ if (vcl::Window* pWindow = pSalWidget->getWidget())
{ if (QtFrame* pFrame = static_cast<QtFrame*>(pWindow->ImplGetFrame())) return pFrame->GetQWidget();
}
}
staticvoid initResources()
{ #ifdefined EMSCRIPTEN && defined DISABLE_DYNLOADING && ENABLE_QT6 // Make sure the resources from Qt6's plugins/platforms/libqwasm.a are not stripped out of a // statically linked binary (and this code cannot be directly in extern "C" create_SalInstance, // as the expansion of Q_INIT_RESOURCE contains extern function declarations that would then // erroneously be C function declarations):
Q_INIT_RESOURCE(wasmfonts);
Q_INIT_RESOURCE(wasmwindow); #endif
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.