backends/drm: Use DrmOutput to represent non-desktop outputs too
The main motivation behind this change is to make the drm backend multi-purpose. That's it, to make it suitable for implementing all kinds of compositors. At the moment, there's an artificial split between "desktop" and "non-desktop" outputs, i.e. VR headsets, which stands in the way of that and moving the remaining wayland code out of the drm backend for better layering and architecture.
This commit is contained in:
parent
1dddf271d2
commit
8dabeb4709
11 changed files with 96 additions and 160 deletions
|
@ -17,7 +17,6 @@ set(DRM_SOURCES
|
|||
drm_pipeline_legacy.cpp
|
||||
drm_abstract_output.cpp
|
||||
drm_virtual_output.cpp
|
||||
drm_lease_output.cpp
|
||||
drm_qpainter_layer.cpp
|
||||
egl_gbm_backend.cpp
|
||||
egl_gbm_layer.cpp
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#include "abstract_egl_backend.h"
|
||||
#include "drm_backend.h"
|
||||
#include "drm_layer.h"
|
||||
#include "drm_lease_output.h"
|
||||
#include "drm_object_connector.h"
|
||||
#include "drm_object_crtc.h"
|
||||
#include "drm_object_plane.h"
|
||||
|
@ -116,9 +115,9 @@ DrmGpu::DrmGpu(DrmBackend *backend, const QString &devNode, int fd, dev_t device
|
|||
|
||||
DrmGpu::~DrmGpu()
|
||||
{
|
||||
const auto leaseOutputs = m_leaseOutputs;
|
||||
const auto leaseOutputs = m_nonDesktopOutputs;
|
||||
for (const auto &output : leaseOutputs) {
|
||||
removeLeaseOutput(output);
|
||||
removeNonDesktopOutput(output);
|
||||
}
|
||||
delete m_leaseDevice;
|
||||
waitIdle();
|
||||
|
@ -232,7 +231,7 @@ bool DrmGpu::updateOutputs()
|
|||
// In principle these things are supposed to be detected through the wayland protocol.
|
||||
// In practice SteamVR doesn't always behave correctly
|
||||
auto lessees = drmModeListLessees(m_fd);
|
||||
for (const auto &leaseOutput : qAsConst(m_leaseOutputs)) {
|
||||
for (const auto &leaseOutput : qAsConst(m_nonDesktopOutputs)) {
|
||||
if (leaseOutput->lease()) {
|
||||
bool leaseActive = false;
|
||||
for (uint i = 0; i < lessees->count; i++) {
|
||||
|
@ -271,24 +270,24 @@ bool DrmGpu::updateOutputs()
|
|||
for (auto it = m_connectors.begin(); it != m_connectors.end();) {
|
||||
DrmConnector *conn = it->get();
|
||||
const auto output = findOutput(conn->id());
|
||||
const auto leaseOutput = findLeaseOutput(conn->id());
|
||||
const auto nonDesktopOutput = findNonDesktopOutput(conn->id());
|
||||
const bool stillExists = existing.contains(conn);
|
||||
if (!stillExists || !conn->isConnected()) {
|
||||
if (output) {
|
||||
removeOutput(output);
|
||||
} else if (leaseOutput) {
|
||||
removeLeaseOutput(leaseOutput);
|
||||
} else if (nonDesktopOutput) {
|
||||
removeNonDesktopOutput(nonDesktopOutput);
|
||||
}
|
||||
conn->disable();
|
||||
} else if (!output && !leaseOutput) {
|
||||
} else if (!output && !nonDesktopOutput) {
|
||||
qCDebug(KWIN_DRM, "New %soutput on GPU %s: %s", conn->isNonDesktop() ? "non-desktop " : "", qPrintable(m_devNode), qPrintable(conn->modelName()));
|
||||
const auto pipeline = conn->pipeline();
|
||||
m_pipelines << pipeline;
|
||||
if (conn->isNonDesktop()) {
|
||||
auto leaseOutput = new DrmLeaseOutput(pipeline, m_leaseDevice);
|
||||
m_leaseOutputs << leaseOutput;
|
||||
auto output = new DrmOutput(pipeline, m_leaseDevice);
|
||||
m_nonDesktopOutputs << output;
|
||||
} else {
|
||||
auto output = new DrmOutput(pipeline);
|
||||
auto output = new DrmOutput(pipeline, m_leaseDevice);
|
||||
m_drmOutputs << output;
|
||||
m_outputs << output;
|
||||
addedOutputs << output;
|
||||
|
@ -416,7 +415,7 @@ DrmPipeline::Error DrmGpu::testPendingConfiguration()
|
|||
QVector<DrmCrtc *> crtcs;
|
||||
// only change resources that aren't currently leased away
|
||||
for (const auto &conn : m_connectors) {
|
||||
bool isLeased = std::any_of(m_leaseOutputs.cbegin(), m_leaseOutputs.cend(), [&conn](const auto output) {
|
||||
bool isLeased = std::any_of(m_nonDesktopOutputs.cbegin(), m_nonDesktopOutputs.cend(), [&conn](const auto output) {
|
||||
return output->lease() && output->pipeline()->connector() == conn.get();
|
||||
});
|
||||
if (!isLeased) {
|
||||
|
@ -424,7 +423,7 @@ DrmPipeline::Error DrmGpu::testPendingConfiguration()
|
|||
}
|
||||
}
|
||||
for (const auto &crtc : m_crtcs) {
|
||||
bool isLeased = std::any_of(m_leaseOutputs.cbegin(), m_leaseOutputs.cend(), [&crtc](const auto output) {
|
||||
bool isLeased = std::any_of(m_nonDesktopOutputs.cbegin(), m_nonDesktopOutputs.cend(), [&crtc](const auto output) {
|
||||
return output->lease() && output->pipeline()->crtc() == crtc.get();
|
||||
});
|
||||
if (!isLeased) {
|
||||
|
@ -624,12 +623,12 @@ void DrmGpu::removeVirtualOutput(DrmVirtualOutput *output)
|
|||
}
|
||||
}
|
||||
|
||||
DrmLeaseOutput *DrmGpu::findLeaseOutput(quint32 connector)
|
||||
DrmOutput *DrmGpu::findNonDesktopOutput(quint32 connector)
|
||||
{
|
||||
auto it = std::find_if(m_leaseOutputs.constBegin(), m_leaseOutputs.constEnd(), [connector](DrmLeaseOutput *o) {
|
||||
auto it = std::find_if(m_nonDesktopOutputs.constBegin(), m_nonDesktopOutputs.constEnd(), [connector](DrmOutput *o) {
|
||||
return o->pipeline()->connector()->id() == connector;
|
||||
});
|
||||
if (it != m_leaseOutputs.constEnd()) {
|
||||
if (it != m_nonDesktopOutputs.constEnd()) {
|
||||
return *it;
|
||||
}
|
||||
return nullptr;
|
||||
|
@ -638,11 +637,11 @@ DrmLeaseOutput *DrmGpu::findLeaseOutput(quint32 connector)
|
|||
void DrmGpu::handleLeaseRequest(KWaylandServer::DrmLeaseV1Interface *leaseRequest)
|
||||
{
|
||||
QVector<uint32_t> objects;
|
||||
QVector<DrmLeaseOutput *> outputs;
|
||||
QVector<DrmOutput *> outputs;
|
||||
|
||||
const auto connectors = leaseRequest->connectors();
|
||||
for (KWaylandServer::DrmLeaseConnectorV1Interface *connector : connectors) {
|
||||
if (DrmLeaseOutput *output = findLeaseOutput(connector->id())) {
|
||||
if (DrmOutput *output = findNonDesktopOutput(connector->id())) {
|
||||
if (output->lease()) {
|
||||
continue; // already leased
|
||||
}
|
||||
|
@ -679,7 +678,7 @@ void DrmGpu::handleLeaseRevoked(KWaylandServer::DrmLeaseV1Interface *lease)
|
|||
{
|
||||
const auto connectors = lease->connectors();
|
||||
for (KWaylandServer::DrmLeaseConnectorV1Interface *connector : connectors) {
|
||||
if (DrmLeaseOutput *output = findLeaseOutput(connector->id())) {
|
||||
if (DrmOutput *output = findNonDesktopOutput(connector->id())) {
|
||||
output->leaseEnded();
|
||||
}
|
||||
}
|
||||
|
@ -687,10 +686,10 @@ void DrmGpu::handleLeaseRevoked(KWaylandServer::DrmLeaseV1Interface *lease)
|
|||
drmModeRevokeLease(m_fd, lease->lesseeId());
|
||||
}
|
||||
|
||||
void DrmGpu::removeLeaseOutput(DrmLeaseOutput *output)
|
||||
void DrmGpu::removeNonDesktopOutput(DrmOutput *output)
|
||||
{
|
||||
qCDebug(KWIN_DRM) << "Removing leased output" << output;
|
||||
m_leaseOutputs.removeOne(output);
|
||||
m_nonDesktopOutputs.removeOne(output);
|
||||
m_pipelines.removeOne(output->pipeline());
|
||||
output->pipeline()->setLayers(nullptr, nullptr);
|
||||
delete output;
|
||||
|
@ -759,7 +758,7 @@ bool DrmGpu::needsModeset() const
|
|||
bool DrmGpu::maybeModeset()
|
||||
{
|
||||
auto pipelines = m_pipelines;
|
||||
for (const auto &output : qAsConst(m_leaseOutputs)) {
|
||||
for (const auto &output : qAsConst(m_nonDesktopOutputs)) {
|
||||
if (output->lease()) {
|
||||
pipelines.removeOne(output->pipeline());
|
||||
}
|
||||
|
|
|
@ -41,7 +41,6 @@ class DrmPlane;
|
|||
class DrmBackend;
|
||||
class EglGbmBackend;
|
||||
class DrmAbstractOutput;
|
||||
class DrmLeaseOutput;
|
||||
class DrmRenderBackend;
|
||||
|
||||
class DrmGpu : public QObject
|
||||
|
@ -94,9 +93,9 @@ Q_SIGNALS:
|
|||
private:
|
||||
void dispatchEvents();
|
||||
DrmOutput *findOutput(quint32 connector);
|
||||
DrmLeaseOutput *findLeaseOutput(quint32 connector);
|
||||
DrmOutput *findNonDesktopOutput(quint32 connector);
|
||||
void removeOutput(DrmOutput *output);
|
||||
void removeLeaseOutput(DrmLeaseOutput *output);
|
||||
void removeNonDesktopOutput(DrmOutput *output);
|
||||
void initDrmResources();
|
||||
void waitIdle();
|
||||
|
||||
|
@ -129,7 +128,7 @@ private:
|
|||
|
||||
QVector<DrmOutput *> m_drmOutputs;
|
||||
QVector<DrmAbstractOutput *> m_outputs;
|
||||
QVector<DrmLeaseOutput *> m_leaseOutputs;
|
||||
QVector<DrmOutput *> m_nonDesktopOutputs;
|
||||
KWaylandServer::DrmLeaseDeviceV1Interface *m_leaseDevice = nullptr;
|
||||
|
||||
QSocketNotifier *m_socketNotifier = nullptr;
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2021 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "drm_lease_output.h"
|
||||
|
||||
#include "drm_layer.h"
|
||||
#include "drm_object_connector.h"
|
||||
#include "drm_object_crtc.h"
|
||||
#include "drm_object_plane.h"
|
||||
#include "drm_pipeline.h"
|
||||
#include "wayland/drmleasedevice_v1_interface.h"
|
||||
|
||||
#include "logging.h"
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
DrmLeaseOutput::DrmLeaseOutput(DrmPipeline *pipeline, KWaylandServer::DrmLeaseDeviceV1Interface *leaseDevice)
|
||||
: m_pipeline(pipeline)
|
||||
{
|
||||
const DrmConnector *connector = pipeline->connector();
|
||||
qCDebug(KWIN_DRM) << "offering connector" << connector->id() << "for lease";
|
||||
|
||||
m_offer = std::make_unique<KWaylandServer::DrmLeaseConnectorV1Interface>(
|
||||
leaseDevice,
|
||||
connector->id(),
|
||||
connector->modelName(),
|
||||
QStringLiteral("%1 %2").arg(connector->edid()->manufacturerString(), connector->modelName()));
|
||||
}
|
||||
|
||||
DrmLeaseOutput::~DrmLeaseOutput()
|
||||
{
|
||||
qCDebug(KWIN_DRM) << "revoking lease offer for connector" << m_pipeline->connector()->id();
|
||||
}
|
||||
|
||||
bool DrmLeaseOutput::addLeaseObjects(QVector<uint32_t> &objectList)
|
||||
{
|
||||
if (!m_pipeline->crtc()) {
|
||||
qCWarning(KWIN_DRM) << "Can't lease connector: No suitable crtc available";
|
||||
return false;
|
||||
}
|
||||
qCDebug(KWIN_DRM) << "adding connector" << m_pipeline->connector()->id() << "to lease";
|
||||
objectList << m_pipeline->connector()->id();
|
||||
objectList << m_pipeline->crtc()->id();
|
||||
if (m_pipeline->crtc()->primaryPlane()) {
|
||||
objectList << m_pipeline->crtc()->primaryPlane()->id();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DrmLeaseOutput::leased(KWaylandServer::DrmLeaseV1Interface *lease)
|
||||
{
|
||||
m_lease = lease;
|
||||
}
|
||||
|
||||
void DrmLeaseOutput::leaseEnded()
|
||||
{
|
||||
qCDebug(KWIN_DRM) << "ended lease for connector" << m_pipeline->connector()->id();
|
||||
m_lease = nullptr;
|
||||
}
|
||||
|
||||
KWaylandServer::DrmLeaseV1Interface *DrmLeaseOutput::lease() const
|
||||
{
|
||||
return m_lease;
|
||||
}
|
||||
|
||||
DrmPipeline *DrmLeaseOutput::pipeline() const
|
||||
{
|
||||
return m_pipeline;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
/*
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
SPDX-FileCopyrightText: 2021 Xaver Hugl <xaver.hugl@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "wayland/drmleasedevice_v1_interface.h"
|
||||
#include <QObject>
|
||||
#include <QVector>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class DrmConnector;
|
||||
class DrmPipeline;
|
||||
|
||||
/**
|
||||
* a DrmLeaseOutput represents a non-desktop output (usually a VR headset)
|
||||
* that is not used directly by the compositor but is instead leased out to
|
||||
* applications (usually VR compositors) that drive the output themselves
|
||||
*/
|
||||
class DrmLeaseOutput
|
||||
{
|
||||
public:
|
||||
DrmLeaseOutput(DrmPipeline *pipeline, KWaylandServer::DrmLeaseDeviceV1Interface *leaseDevice);
|
||||
~DrmLeaseOutput();
|
||||
|
||||
bool addLeaseObjects(QVector<uint32_t> &objectList);
|
||||
void leased(KWaylandServer::DrmLeaseV1Interface *lease);
|
||||
void leaseEnded();
|
||||
|
||||
KWaylandServer::DrmLeaseV1Interface *lease() const;
|
||||
DrmPipeline *pipeline() const;
|
||||
|
||||
private:
|
||||
DrmPipeline *m_pipeline;
|
||||
std::unique_ptr<KWaylandServer::DrmLeaseConnectorV1Interface> m_offer;
|
||||
KWaylandServer::DrmLeaseV1Interface *m_lease = nullptr;
|
||||
};
|
||||
|
||||
}
|
|
@ -28,6 +28,7 @@
|
|||
#include "renderloop_p.h"
|
||||
#include "scene.h"
|
||||
#include "session.h"
|
||||
#include "wayland/drmleasedevice_v1_interface.h"
|
||||
// Qt
|
||||
#include <QCryptographicHash>
|
||||
#include <QMatrix4x4>
|
||||
|
@ -42,7 +43,7 @@
|
|||
namespace KWin
|
||||
{
|
||||
|
||||
DrmOutput::DrmOutput(DrmPipeline *pipeline)
|
||||
DrmOutput::DrmOutput(DrmPipeline *pipeline, KWaylandServer::DrmLeaseDeviceV1Interface *leaseDevice)
|
||||
: DrmAbstractOutput(pipeline->connector()->gpu())
|
||||
, m_pipeline(pipeline)
|
||||
, m_connector(pipeline->connector())
|
||||
|
@ -78,6 +79,7 @@ DrmOutput::DrmOutput(DrmPipeline *pipeline)
|
|||
.subPixel = conn->subpixel(),
|
||||
.capabilities = capabilities,
|
||||
.internal = conn->isInternal(),
|
||||
.nonDesktop = conn->isNonDesktop(),
|
||||
});
|
||||
|
||||
const QList<std::shared_ptr<OutputMode>> modes = getModes();
|
||||
|
@ -93,9 +95,17 @@ DrmOutput::DrmOutput(DrmPipeline *pipeline)
|
|||
setDrmDpmsMode(DpmsMode::Off);
|
||||
});
|
||||
|
||||
connect(Cursors::self(), &Cursors::currentCursorChanged, this, &DrmOutput::updateCursor);
|
||||
connect(Cursors::self(), &Cursors::hiddenChanged, this, &DrmOutput::updateCursor);
|
||||
connect(Cursors::self(), &Cursors::positionChanged, this, &DrmOutput::moveCursor);
|
||||
if (conn->isNonDesktop()) {
|
||||
m_offer = std::make_unique<KWaylandServer::DrmLeaseConnectorV1Interface>(
|
||||
leaseDevice,
|
||||
conn->id(),
|
||||
conn->modelName(),
|
||||
QStringLiteral("%1 %2").arg(conn->edid()->manufacturerString(), conn->modelName()));
|
||||
} else {
|
||||
connect(Cursors::self(), &Cursors::currentCursorChanged, this, &DrmOutput::updateCursor);
|
||||
connect(Cursors::self(), &Cursors::hiddenChanged, this, &DrmOutput::updateCursor);
|
||||
connect(Cursors::self(), &Cursors::positionChanged, this, &DrmOutput::moveCursor);
|
||||
}
|
||||
}
|
||||
|
||||
DrmOutput::~DrmOutput()
|
||||
|
@ -103,6 +113,40 @@ DrmOutput::~DrmOutput()
|
|||
m_pipeline->setOutput(nullptr);
|
||||
}
|
||||
|
||||
bool DrmOutput::addLeaseObjects(QVector<uint32_t> &objectList)
|
||||
{
|
||||
Q_ASSERT(m_offer);
|
||||
if (!m_pipeline->crtc()) {
|
||||
qCWarning(KWIN_DRM) << "Can't lease connector: No suitable crtc available";
|
||||
return false;
|
||||
}
|
||||
qCDebug(KWIN_DRM) << "adding connector" << m_pipeline->connector()->id() << "to lease";
|
||||
objectList << m_pipeline->connector()->id();
|
||||
objectList << m_pipeline->crtc()->id();
|
||||
if (m_pipeline->crtc()->primaryPlane()) {
|
||||
objectList << m_pipeline->crtc()->primaryPlane()->id();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DrmOutput::leased(KWaylandServer::DrmLeaseV1Interface *lease)
|
||||
{
|
||||
Q_ASSERT(m_offer);
|
||||
m_lease = lease;
|
||||
}
|
||||
|
||||
void DrmOutput::leaseEnded()
|
||||
{
|
||||
Q_ASSERT(m_offer);
|
||||
qCDebug(KWIN_DRM) << "ended lease for connector" << m_pipeline->connector()->id();
|
||||
m_lease = nullptr;
|
||||
}
|
||||
|
||||
KWaylandServer::DrmLeaseV1Interface *DrmOutput::lease() const
|
||||
{
|
||||
return m_lease;
|
||||
}
|
||||
|
||||
void DrmOutput::updateCursor()
|
||||
{
|
||||
static bool valid;
|
||||
|
|
|
@ -21,6 +21,13 @@
|
|||
#include <chrono>
|
||||
#include <xf86drmMode.h>
|
||||
|
||||
namespace KWaylandServer
|
||||
{
|
||||
class DrmLeaseConnectorV1Interface;
|
||||
class DrmLeaseDeviceV1Interface;
|
||||
class DrmLeaseV1Interface;
|
||||
}
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
|
@ -35,7 +42,7 @@ class KWIN_EXPORT DrmOutput : public DrmAbstractOutput
|
|||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
DrmOutput(DrmPipeline *pipeline);
|
||||
DrmOutput(DrmPipeline *pipeline, KWaylandServer::DrmLeaseDeviceV1Interface *leaseDevice);
|
||||
~DrmOutput() override;
|
||||
|
||||
DrmConnector *connector() const;
|
||||
|
@ -53,6 +60,11 @@ public:
|
|||
void updateCursor();
|
||||
void moveCursor();
|
||||
|
||||
KWaylandServer::DrmLeaseV1Interface *lease() const;
|
||||
bool addLeaseObjects(QVector<uint32_t> &objectList);
|
||||
void leased(KWaylandServer::DrmLeaseV1Interface *lease);
|
||||
void leaseEnded();
|
||||
|
||||
void setColorTransformation(const std::shared_ptr<ColorTransformation> &transformation) override;
|
||||
|
||||
private:
|
||||
|
@ -73,6 +85,8 @@ private:
|
|||
bool m_cursorTextureDirty = true;
|
||||
std::unique_ptr<GLTexture> m_cursorTexture;
|
||||
QTimer m_turnOffTimer;
|
||||
std::unique_ptr<KWaylandServer::DrmLeaseConnectorV1Interface> m_offer;
|
||||
KWaylandServer::DrmLeaseV1Interface *m_lease = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -240,7 +240,7 @@ EGLConfig EglGbmBackend::config(uint32_t format) const
|
|||
|
||||
std::shared_ptr<DrmPipelineLayer> EglGbmBackend::createPrimaryLayer(DrmPipeline *pipeline)
|
||||
{
|
||||
if (pipeline->output()) {
|
||||
if (!pipeline->output()->isNonDesktop()) {
|
||||
return std::make_shared<EglGbmLayer>(this, pipeline);
|
||||
} else {
|
||||
return std::make_shared<DrmLeaseEglGbmLayer>(pipeline);
|
||||
|
|
|
@ -46,7 +46,7 @@ OutputLayer *DrmQPainterBackend::primaryLayer(Output *output)
|
|||
|
||||
std::shared_ptr<DrmPipelineLayer> DrmQPainterBackend::createPrimaryLayer(DrmPipeline *pipeline)
|
||||
{
|
||||
if (pipeline->output()) {
|
||||
if (!pipeline->output()->isNonDesktop()) {
|
||||
return std::make_shared<DrmQPainterLayer>(pipeline);
|
||||
} else {
|
||||
return std::make_shared<DrmLeaseQPainterLayer>(pipeline);
|
||||
|
|
|
@ -399,6 +399,11 @@ bool Output::isPlaceholder() const
|
|||
return m_information.placeholder;
|
||||
}
|
||||
|
||||
bool Output::isNonDesktop() const
|
||||
{
|
||||
return m_information.nonDesktop;
|
||||
}
|
||||
|
||||
Output::RgbRange Output::rgbRange() const
|
||||
{
|
||||
return m_rgbRange;
|
||||
|
|
|
@ -239,6 +239,7 @@ public:
|
|||
RgbRange rgbRange() const;
|
||||
|
||||
bool isPlaceholder() const;
|
||||
bool isNonDesktop() const;
|
||||
|
||||
virtual void setColorTransformation(const std::shared_ptr<ColorTransformation> &transformation);
|
||||
|
||||
|
@ -310,6 +311,7 @@ protected:
|
|||
Capabilities capabilities;
|
||||
bool internal = false;
|
||||
bool placeholder = false;
|
||||
bool nonDesktop = false;
|
||||
};
|
||||
|
||||
void setInformation(const Information &information);
|
||||
|
|
Loading…
Reference in a new issue