This is a preliminary fix, so LO on Wayland has an application icon at all. As the result, the start center icon will appear as the application icon on Wayland for all windows. For the proper, per QWindow fix, we need some QtCore and QtWayland changes, to allow setting the appId per QWindow and eventually a Waylnad XDG shell spec update, to officially allow a top level window to change its appId. Change-Id: Icefcfc992836e0c657237984d7d88561b26b06a2 Reviewed-on: https://gerrit.libreoffice.org/74097 Tested-by: Jenkins Reviewed-by: Jan-Marek Glogowski <glogow@fbihome.de>
635 lines
21 KiB
C++
635 lines
21 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* 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 .
|
|
*/
|
|
|
|
#include <Qt5Instance.hxx>
|
|
#include <Qt5Instance.moc>
|
|
|
|
#include <com/sun/star/lang/IllegalArgumentException.hpp>
|
|
|
|
#include <Qt5Bitmap.hxx>
|
|
#include <Qt5Clipboard.hxx>
|
|
#include <Qt5Data.hxx>
|
|
#include <Qt5DragAndDrop.hxx>
|
|
#include <Qt5FilePicker.hxx>
|
|
#include <Qt5Frame.hxx>
|
|
#include <Qt5Menu.hxx>
|
|
#include <Qt5Object.hxx>
|
|
#include <Qt5OpenGLContext.hxx>
|
|
#include "Qt5SvpVirtualDevice.hxx"
|
|
#include <Qt5System.hxx>
|
|
#include <Qt5Timer.hxx>
|
|
#include <Qt5VirtualDevice.hxx>
|
|
|
|
#include <headless/svpvd.hxx>
|
|
|
|
#include <QtCore/QAbstractEventDispatcher>
|
|
#include <QtCore/QThread>
|
|
#include <QtWidgets/QApplication>
|
|
#include <QtWidgets/QWidget>
|
|
|
|
#include <vclpluginapi.h>
|
|
#include <tools/debug.hxx>
|
|
#include <comphelper/flagguard.hxx>
|
|
#include <sal/log.hxx>
|
|
#include <osl/process.h>
|
|
#include <unx/gstsink.hxx>
|
|
#include <headless/svpbmp.hxx>
|
|
|
|
#include <mutex>
|
|
#include <condition_variable>
|
|
|
|
/// TODO: not much Qt5 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 Qt5; 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 Qt5YieldMutex : 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;
|
|
|
|
virtual bool IsCurrentThread() const override;
|
|
virtual void doAcquire(sal_uInt32 nLockCount) override;
|
|
virtual sal_uInt32 doRelease(bool const bUnlockAll) override;
|
|
};
|
|
|
|
bool Qt5YieldMutex::IsCurrentThread() const
|
|
{
|
|
auto const* pSalInst(static_cast<Qt5Instance const*>(GetSalData()->m_pInstance));
|
|
assert(pSalInst);
|
|
if (pSalInst->IsMainThread() && m_bNoYieldLock)
|
|
{
|
|
return true; // main thread has borrowed SolarMutex
|
|
}
|
|
return SalYieldMutex::IsCurrentThread();
|
|
}
|
|
|
|
void Qt5YieldMutex::doAcquire(sal_uInt32 nLockCount)
|
|
{
|
|
auto const* pSalInst(static_cast<Qt5Instance const*>(GetSalData()->m_pInstance));
|
|
assert(pSalInst);
|
|
if (!pSalInst->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::unique_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 Qt5YieldMutex::doRelease(bool const bUnlockAll)
|
|
{
|
|
auto const* pSalInst(static_cast<Qt5Instance const*>(GetSalData()->m_pInstance));
|
|
assert(pSalInst);
|
|
if (pSalInst->IsMainThread() && m_bNoYieldLock)
|
|
{
|
|
return 1; // dummy value
|
|
}
|
|
|
|
std::unique_lock<std::mutex> g(m_RunInMainMutex);
|
|
// read m_nCount before doRelease (it's guarded by m_aMutex)
|
|
bool const isReleased(bUnlockAll || m_nCount == 1);
|
|
sal_uInt32 nCount = SalYieldMutex::doRelease(bUnlockAll);
|
|
if (isReleased && !pSalInst->IsMainThread())
|
|
{
|
|
m_isWakeUpMain = true;
|
|
m_InMainCondition.notify_all(); // unblock main thread
|
|
}
|
|
return nCount;
|
|
}
|
|
|
|
// this could be abstracted to be independent of Qt5 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 Qt5Instance::RunInMainThread(std::function<void()> func)
|
|
{
|
|
DBG_TESTSOLARMUTEX();
|
|
if (IsMainThread())
|
|
{
|
|
func();
|
|
return;
|
|
}
|
|
|
|
Qt5YieldMutex* const pMutex(static_cast<Qt5YieldMutex*>(GetYieldMutex()));
|
|
{
|
|
std::unique_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();
|
|
}
|
|
// wake up main thread in case it is blocked on event queue
|
|
// TriggerUserEventProcessing() appears to be insufficient in case the
|
|
// main thread does QEventLoop::WaitForMoreEvents
|
|
Q_EMIT ImplRunInMainSignal();
|
|
{
|
|
std::unique_lock<std::mutex> g(pMutex->m_RunInMainMutex);
|
|
pMutex->m_ResultCondition.wait(g, [pMutex]() { return pMutex->m_isResultReady; });
|
|
pMutex->m_isResultReady = false;
|
|
}
|
|
}
|
|
|
|
void Qt5Instance::ImplRunInMain()
|
|
{
|
|
SolarMutexGuard g; // trigger the dispatch code in Qt5YieldMutex::doAcquire
|
|
(void)this; // suppress unhelpful [loplugin:staticmethods]; can't be static
|
|
}
|
|
|
|
Qt5Instance::Qt5Instance(std::unique_ptr<QApplication>& pQApp, bool bUseCairo)
|
|
: SalGenericInstance(std::make_unique<Qt5YieldMutex>())
|
|
, m_postUserEventId(-1)
|
|
, m_bUseCairo(bUseCairo)
|
|
, m_pQApplication(std::move(pQApp))
|
|
, m_aUpdateStyleTimer("vcl::qt5 m_aUpdateStyleTimer")
|
|
, m_bUpdateFonts(false)
|
|
{
|
|
ImplSVData* pSVData = ImplGetSVData();
|
|
if (bUseCairo)
|
|
pSVData->maAppData.mxToolkitName = OUString("qt5+cairo");
|
|
else
|
|
pSVData->maAppData.mxToolkitName = OUString("qt5");
|
|
|
|
m_postUserEventId = QEvent::registerEventType();
|
|
|
|
// this one needs to be blocking, so that the handling in main thread
|
|
// is processed before the thread emitting the signal continues
|
|
connect(this, SIGNAL(ImplYieldSignal(bool, bool)), this, SLOT(ImplYield(bool, bool)),
|
|
Qt::BlockingQueuedConnection);
|
|
connect(this, &Qt5Instance::ImplRunInMainSignal, this, &Qt5Instance::ImplRunInMain,
|
|
Qt::QueuedConnection); // no Blocking!
|
|
|
|
// this one needs to be queued non-blocking
|
|
// in order to have this event arriving to correct event processing loop
|
|
connect(this, &Qt5Instance::deleteObjectLaterSignal, this,
|
|
[](QObject* pObject) { Qt5Instance::deleteObjectLater(pObject); },
|
|
Qt::QueuedConnection);
|
|
|
|
m_aUpdateStyleTimer.SetTimeout(50);
|
|
m_aUpdateStyleTimer.SetInvokeHandler(LINK(this, Qt5Instance, updateStyleHdl));
|
|
}
|
|
|
|
Qt5Instance::~Qt5Instance()
|
|
{
|
|
// force freeing the QApplication before freeing the arguments,
|
|
// as it uses references to the provided arguments!
|
|
m_pQApplication.reset();
|
|
}
|
|
|
|
void Qt5Instance::AfterAppInit()
|
|
{
|
|
// sets the default application icon on Wayland
|
|
QGuiApplication::setDesktopFileName(QStringLiteral("libreoffice-startcenter.desktop"));
|
|
}
|
|
|
|
void Qt5Instance::deleteObjectLater(QObject* pObject) { pObject->deleteLater(); }
|
|
|
|
SalFrame* Qt5Instance::CreateChildFrame(SystemParentData* /*pParent*/, SalFrameStyleFlags nStyle)
|
|
{
|
|
return new Qt5Frame(nullptr, nStyle, m_bUseCairo);
|
|
}
|
|
|
|
SalFrame* Qt5Instance::CreateFrame(SalFrame* pParent, SalFrameStyleFlags nStyle)
|
|
{
|
|
assert(!pParent || dynamic_cast<Qt5Frame*>(pParent));
|
|
return new Qt5Frame(static_cast<Qt5Frame*>(pParent), nStyle, m_bUseCairo);
|
|
}
|
|
|
|
void Qt5Instance::DestroyFrame(SalFrame* pFrame)
|
|
{
|
|
if (pFrame)
|
|
{
|
|
assert(dynamic_cast<Qt5Frame*>(pFrame));
|
|
Q_EMIT deleteObjectLaterSignal(static_cast<Qt5Frame*>(pFrame));
|
|
}
|
|
}
|
|
|
|
SalObject* Qt5Instance::CreateObject(SalFrame* pParent, SystemWindowData*, bool bShow)
|
|
{
|
|
assert(!pParent || dynamic_cast<Qt5Frame*>(pParent));
|
|
return new Qt5Object(static_cast<Qt5Frame*>(pParent), bShow);
|
|
}
|
|
|
|
void Qt5Instance::DestroyObject(SalObject* pObject)
|
|
{
|
|
if (pObject)
|
|
{
|
|
assert(dynamic_cast<Qt5Object*>(pObject));
|
|
Q_EMIT deleteObjectLaterSignal(static_cast<Qt5Object*>(pObject));
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<SalVirtualDevice>
|
|
Qt5Instance::CreateVirtualDevice(SalGraphics* pGraphics, long& nDX, long& nDY, DeviceFormat eFormat,
|
|
const SystemGraphicsData* /* pData */)
|
|
{
|
|
if (m_bUseCairo)
|
|
{
|
|
SvpSalGraphics* pSvpSalGraphics = dynamic_cast<Qt5SvpGraphics*>(pGraphics);
|
|
assert(pSvpSalGraphics);
|
|
std::unique_ptr<SalVirtualDevice> pVD(
|
|
new Qt5SvpVirtualDevice(eFormat, pSvpSalGraphics->getSurface()));
|
|
pVD->SetSize(nDX, nDY);
|
|
return pVD;
|
|
}
|
|
else
|
|
{
|
|
std::unique_ptr<SalVirtualDevice> pVD(new Qt5VirtualDevice(eFormat, 1));
|
|
pVD->SetSize(nDX, nDY);
|
|
return pVD;
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<SalMenu> Qt5Instance::CreateMenu(bool bMenuBar, Menu* pVCLMenu)
|
|
{
|
|
std::unique_ptr<SalMenu> pRet;
|
|
RunInMainThread([&pRet, bMenuBar, pVCLMenu]() {
|
|
Qt5Menu* pSalMenu = new Qt5Menu(bMenuBar);
|
|
pRet.reset(pSalMenu);
|
|
pSalMenu->SetMenu(pVCLMenu);
|
|
});
|
|
assert(pRet);
|
|
return pRet;
|
|
}
|
|
|
|
std::unique_ptr<SalMenuItem> Qt5Instance::CreateMenuItem(const SalItemParams& rItemData)
|
|
{
|
|
return std::unique_ptr<SalMenuItem>(new Qt5MenuItem(&rItemData));
|
|
}
|
|
|
|
SalTimer* Qt5Instance::CreateSalTimer() { return new Qt5Timer(); }
|
|
|
|
SalSystem* Qt5Instance::CreateSalSystem() { return new Qt5System; }
|
|
|
|
std::shared_ptr<SalBitmap> Qt5Instance::CreateSalBitmap()
|
|
{
|
|
if (m_bUseCairo)
|
|
return std::make_shared<SvpSalBitmap>();
|
|
else
|
|
return std::make_shared<Qt5Bitmap>();
|
|
}
|
|
|
|
bool Qt5Instance::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)
|
|
return true;
|
|
|
|
/**
|
|
* 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;
|
|
}
|
|
|
|
bool Qt5Instance::DoYield(bool bWait, bool bHandleAllCurrentEvents)
|
|
{
|
|
bool bWasEvent = false;
|
|
if (qApp->thread() == QThread::currentThread())
|
|
{
|
|
bWasEvent = ImplYield(bWait, bHandleAllCurrentEvents);
|
|
if (bWasEvent)
|
|
m_aWaitingYieldCond.set();
|
|
}
|
|
else
|
|
{
|
|
{
|
|
SolarMutexReleaser aReleaser;
|
|
bWasEvent = Q_EMIT ImplYieldSignal(false, bHandleAllCurrentEvents);
|
|
}
|
|
if (!bWasEvent && bWait)
|
|
{
|
|
m_aWaitingYieldCond.reset();
|
|
SolarMutexReleaser aReleaser;
|
|
m_aWaitingYieldCond.wait();
|
|
bWasEvent = true;
|
|
}
|
|
}
|
|
return bWasEvent;
|
|
}
|
|
|
|
bool Qt5Instance::AnyInput(VclInputFlags /*nType*/) { return false; }
|
|
|
|
OUString Qt5Instance::GetConnectionIdentifier() { return OUString(); }
|
|
|
|
void Qt5Instance::AddToRecentDocumentList(const OUString&, const OUString&, const OUString&) {}
|
|
|
|
OpenGLContext* Qt5Instance::CreateOpenGLContext() { return new Qt5OpenGLContext; }
|
|
|
|
bool Qt5Instance::IsMainThread() const { return qApp->thread() == QThread::currentThread(); }
|
|
|
|
void Qt5Instance::TriggerUserEventProcessing()
|
|
{
|
|
QApplication::postEvent(this, new QEvent(QEvent::Type(m_postUserEventId)));
|
|
}
|
|
|
|
void Qt5Instance::ProcessEvent(SalUserEvent aEvent)
|
|
{
|
|
aEvent.m_pFrame->CallCallback(aEvent.m_nEvent, aEvent.m_pData);
|
|
}
|
|
|
|
Qt5FilePicker* Qt5Instance::createPicker(QFileDialog::FileMode eMode)
|
|
{
|
|
if (!IsMainThread())
|
|
{
|
|
SolarMutexGuard g;
|
|
Qt5FilePicker* pPicker;
|
|
RunInMainThread(std::function([&, this]() { pPicker = createPicker(eMode); }));
|
|
assert(pPicker);
|
|
return pPicker;
|
|
}
|
|
|
|
return new Qt5FilePicker(eMode);
|
|
}
|
|
|
|
css::uno::Reference<css::ui::dialogs::XFilePicker2>
|
|
Qt5Instance::createFilePicker(const css::uno::Reference<css::uno::XComponentContext>&)
|
|
{
|
|
return css::uno::Reference<css::ui::dialogs::XFilePicker2>(
|
|
createPicker(QFileDialog::ExistingFile));
|
|
}
|
|
|
|
css::uno::Reference<css::ui::dialogs::XFolderPicker2>
|
|
Qt5Instance::createFolderPicker(const css::uno::Reference<css::uno::XComponentContext>&)
|
|
{
|
|
return css::uno::Reference<css::ui::dialogs::XFolderPicker2>(
|
|
createPicker(QFileDialog::Directory));
|
|
}
|
|
|
|
css::uno::Reference<css::uno::XInterface>
|
|
Qt5Instance::CreateClipboard(const css::uno::Sequence<css::uno::Any>& arguments)
|
|
{
|
|
OUString sel;
|
|
if (arguments.getLength() == 0)
|
|
{
|
|
sel = "CLIPBOARD";
|
|
}
|
|
else if (arguments.getLength() != 1 || !(arguments[0] >>= sel))
|
|
{
|
|
throw css::lang::IllegalArgumentException("bad Qt5Instance::CreateClipboard arguments",
|
|
css::uno::Reference<css::uno::XInterface>(), -1);
|
|
}
|
|
|
|
// 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;
|
|
|
|
css::uno::Reference<css::uno::XInterface> xClipboard = Qt5Clipboard::create(sel);
|
|
if (xClipboard.is())
|
|
m_aClipboards[sel] = xClipboard;
|
|
|
|
return xClipboard;
|
|
}
|
|
|
|
css::uno::Reference<css::uno::XInterface> Qt5Instance::CreateDragSource()
|
|
{
|
|
return css::uno::Reference<css::uno::XInterface>(
|
|
static_cast<cppu::OWeakObject*>(new Qt5DragSource()));
|
|
}
|
|
|
|
css::uno::Reference<css::uno::XInterface> Qt5Instance::CreateDropTarget()
|
|
{
|
|
return css::uno::Reference<css::uno::XInterface>(
|
|
static_cast<cppu::OWeakObject*>(new Qt5DropTarget()));
|
|
}
|
|
|
|
IMPL_LINK_NOARG(Qt5Instance, updateStyleHdl, Timer*, void)
|
|
{
|
|
SolarMutexGuard aGuard;
|
|
SalFrame* pFrame = anyFrame();
|
|
if (pFrame)
|
|
{
|
|
pFrame->CallCallback(SalEvent::SettingsChanged, nullptr);
|
|
if (m_bUpdateFonts)
|
|
{
|
|
pFrame->CallCallback(SalEvent::FontChanged, nullptr);
|
|
m_bUpdateFonts = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Qt5Instance::UpdateStyle(bool bFontsChanged)
|
|
{
|
|
if (bFontsChanged)
|
|
m_bUpdateFonts = true;
|
|
if (!m_aUpdateStyleTimer.IsActive())
|
|
m_aUpdateStyleTimer.Start();
|
|
}
|
|
|
|
void* Qt5Instance::CreateGStreamerSink(const SystemChildWindow* pWindow)
|
|
{
|
|
#if ENABLE_GSTREAMER_1_0 && QT5_HAVE_GOBJECT
|
|
auto pSymbol = gstElementFactoryNameSymbol();
|
|
if (!pSymbol)
|
|
return nullptr;
|
|
|
|
const SystemEnvData* pEnvData = pWindow->GetSystemData();
|
|
if (!pEnvData)
|
|
return nullptr;
|
|
|
|
OUString aPlatform = OUString::createFromAscii(pEnvData->pPlatformName);
|
|
if (aPlatform != "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.qt5", "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
|
|
}
|
|
|
|
return pVideosink;
|
|
#else
|
|
(void*)pWindow;
|
|
return nullptr;
|
|
#endif
|
|
}
|
|
|
|
void Qt5Instance::AllocFakeCmdlineArgs(std::unique_ptr<char* []>& rFakeArgv,
|
|
std::unique_ptr<int>& rFakeArgc,
|
|
std::vector<FreeableCStr>& rFakeArgvFreeable)
|
|
{
|
|
OString aVersion(qVersion());
|
|
SAL_INFO("vcl.qt5", "qt version string is " << aVersion);
|
|
|
|
const sal_uInt32 nParams = osl_getCommandArgCount();
|
|
OString aDisplay;
|
|
sal_uInt32 nDisplayValueIdx = 0;
|
|
OUString aParam, aBin;
|
|
|
|
for (sal_uInt32 nIdx = 0; nIdx < nParams; ++nIdx)
|
|
{
|
|
osl_getCommandArg(nIdx, &aParam.pData);
|
|
if (aParam != "-display")
|
|
continue;
|
|
++nIdx;
|
|
nDisplayValueIdx = nIdx;
|
|
}
|
|
|
|
osl_getExecutableFile(&aParam.pData);
|
|
osl_getSystemPathFromFileURL(aParam.pData, &aBin.pData);
|
|
OString aExec = OUStringToOString(aBin, osl_getThreadTextEncoding());
|
|
|
|
std::vector<FreeableCStr> aFakeArgvFreeable;
|
|
aFakeArgvFreeable.reserve(4);
|
|
aFakeArgvFreeable.emplace_back(strdup(aExec.getStr()));
|
|
aFakeArgvFreeable.emplace_back(strdup("--nocrashhandler"));
|
|
if (nDisplayValueIdx)
|
|
{
|
|
aFakeArgvFreeable.emplace_back(strdup("-display"));
|
|
osl_getCommandArg(nDisplayValueIdx, &aParam.pData);
|
|
aDisplay = OUStringToOString(aParam, osl_getThreadTextEncoding());
|
|
aFakeArgvFreeable.emplace_back(strdup(aDisplay.getStr()));
|
|
}
|
|
rFakeArgvFreeable.swap(aFakeArgvFreeable);
|
|
|
|
const int nFakeArgc = rFakeArgvFreeable.size();
|
|
rFakeArgv.reset(new char*[nFakeArgc]);
|
|
for (int i = 0; i < nFakeArgc; i++)
|
|
rFakeArgv[i] = rFakeArgvFreeable[i].get();
|
|
|
|
rFakeArgc.reset(new int);
|
|
*rFakeArgc = nFakeArgc;
|
|
}
|
|
|
|
void Qt5Instance::MoveFakeCmdlineArgs(std::unique_ptr<char* []>& rFakeArgv,
|
|
std::unique_ptr<int>& rFakeArgc,
|
|
std::vector<FreeableCStr>& rFakeArgvFreeable)
|
|
{
|
|
m_pFakeArgv = std::move(rFakeArgv);
|
|
m_pFakeArgc = std::move(rFakeArgc);
|
|
m_pFakeArgvFreeable.swap(rFakeArgvFreeable);
|
|
}
|
|
|
|
std::unique_ptr<QApplication> Qt5Instance::CreateQApplication(int& nArgc, char** pArgv)
|
|
{
|
|
QApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
|
|
|
|
FreeableCStr session_manager;
|
|
if (getenv("SESSION_MANAGER") != nullptr)
|
|
{
|
|
session_manager.reset(strdup(getenv("SESSION_MANAGER")));
|
|
unsetenv("SESSION_MANAGER");
|
|
}
|
|
|
|
std::unique_ptr<QApplication> pQApp = std::make_unique<QApplication>(nArgc, pArgv);
|
|
|
|
if (session_manager != nullptr)
|
|
{
|
|
// coverity[tainted_string] - trusted source for setenv
|
|
setenv("SESSION_MANAGER", session_manager.get(), 1);
|
|
}
|
|
|
|
QApplication::setQuitOnLastWindowClosed(false);
|
|
return pQApp;
|
|
}
|
|
|
|
extern "C" {
|
|
VCLPLUG_QT5_PUBLIC SalInstance* create_SalInstance()
|
|
{
|
|
static const bool bUseCairo = (nullptr != getenv("SAL_VCL_QT5_USE_CAIRO"));
|
|
|
|
std::unique_ptr<char* []> pFakeArgv;
|
|
std::unique_ptr<int> pFakeArgc;
|
|
std::vector<FreeableCStr> aFakeArgvFreeable;
|
|
Qt5Instance::AllocFakeCmdlineArgs(pFakeArgv, pFakeArgc, aFakeArgvFreeable);
|
|
|
|
std::unique_ptr<QApplication> pQApp
|
|
= Qt5Instance::CreateQApplication(*pFakeArgc, pFakeArgv.get());
|
|
|
|
Qt5Instance* pInstance = new Qt5Instance(pQApp, bUseCairo);
|
|
pInstance->MoveFakeCmdlineArgs(pFakeArgv, pFakeArgc, aFakeArgvFreeable);
|
|
|
|
new Qt5Data(pInstance);
|
|
|
|
return pInstance;
|
|
}
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|