2020-08-02 22:22:19 +00:00
|
|
|
/*
|
|
|
|
KWin - the KDE window manager
|
|
|
|
This file is part of the KDE project.
|
2019-06-13 09:36:07 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com>
|
|
|
|
SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org>
|
2019-06-13 09:36:07 +00:00
|
|
|
|
2020-08-02 22:22:19 +00:00
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
2019-06-13 09:36:07 +00:00
|
|
|
#include "abstract_wayland_output.h"
|
2020-01-02 14:55:03 +00:00
|
|
|
#include "screens.h"
|
2019-06-13 09:36:07 +00:00
|
|
|
|
|
|
|
// KWayland
|
2020-04-29 15:18:41 +00:00
|
|
|
#include <KWaylandServer/outputchangeset.h>
|
2019-06-13 09:36:07 +00:00
|
|
|
// KF5
|
|
|
|
#include <KLocalizedString>
|
|
|
|
|
2020-08-12 16:52:08 +00:00
|
|
|
#include <QMatrix4x4>
|
2019-06-13 09:36:07 +00:00
|
|
|
|
|
|
|
namespace KWin
|
|
|
|
{
|
|
|
|
|
2021-04-04 14:11:13 +00:00
|
|
|
static AbstractWaylandOutput::Transform outputDeviceTransformToKWinTransform(KWaylandServer::OutputDeviceInterface::Transform transform)
|
|
|
|
{
|
|
|
|
return static_cast<AbstractWaylandOutput::Transform>(transform);
|
|
|
|
}
|
|
|
|
|
2019-06-13 09:36:07 +00:00
|
|
|
AbstractWaylandOutput::AbstractWaylandOutput(QObject *parent)
|
|
|
|
: AbstractOutput(parent)
|
|
|
|
{
|
2021-04-04 14:11:13 +00:00
|
|
|
}
|
2020-08-13 15:32:17 +00:00
|
|
|
|
2021-04-04 14:11:13 +00:00
|
|
|
AbstractWaylandOutput::Capabilities AbstractWaylandOutput::capabilities() const
|
|
|
|
{
|
|
|
|
return m_capabilities;
|
2019-06-13 09:36:07 +00:00
|
|
|
}
|
|
|
|
|
2021-04-04 14:11:13 +00:00
|
|
|
void AbstractWaylandOutput::setCapabilityInternal(Capability capability, bool on)
|
2019-06-13 09:36:07 +00:00
|
|
|
{
|
2021-04-11 14:26:21 +00:00
|
|
|
if (static_cast<bool>(m_capabilities & capability) != on) {
|
|
|
|
m_capabilities.setFlag(capability, on);
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT capabilitiesChanged();
|
2021-04-11 14:26:21 +00:00
|
|
|
}
|
2019-06-13 09:36:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QString AbstractWaylandOutput::name() const
|
|
|
|
{
|
2020-04-08 09:38:41 +00:00
|
|
|
return m_name;
|
2019-06-13 09:36:07 +00:00
|
|
|
}
|
|
|
|
|
2021-04-06 20:08:02 +00:00
|
|
|
QUuid AbstractWaylandOutput::uuid() const
|
2019-08-28 18:54:37 +00:00
|
|
|
{
|
2021-04-04 14:11:13 +00:00
|
|
|
return m_uuid;
|
2019-08-28 18:54:37 +00:00
|
|
|
}
|
|
|
|
|
2019-06-13 09:36:07 +00:00
|
|
|
QRect AbstractWaylandOutput::geometry() const
|
|
|
|
{
|
2019-08-27 14:19:47 +00:00
|
|
|
return QRect(globalPos(), pixelSize() / scale());
|
2019-06-13 09:36:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QSize AbstractWaylandOutput::physicalSize() const
|
|
|
|
{
|
2021-04-04 14:11:13 +00:00
|
|
|
return orientateSize(m_physicalSize);
|
2019-06-13 09:36:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int AbstractWaylandOutput::refreshRate() const
|
|
|
|
{
|
2021-04-04 14:11:13 +00:00
|
|
|
return m_refreshRate;
|
2019-06-13 09:36:07 +00:00
|
|
|
}
|
|
|
|
|
2019-08-27 12:41:16 +00:00
|
|
|
QPoint AbstractWaylandOutput::globalPos() const
|
|
|
|
{
|
2021-04-04 14:11:13 +00:00
|
|
|
return m_position;
|
2019-08-27 12:41:16 +00:00
|
|
|
}
|
|
|
|
|
2019-06-13 09:36:07 +00:00
|
|
|
void AbstractWaylandOutput::setGlobalPos(const QPoint &pos)
|
|
|
|
{
|
2021-04-04 14:11:13 +00:00
|
|
|
if (m_position != pos) {
|
|
|
|
m_position = pos;
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT geometryChanged();
|
2021-04-04 14:11:13 +00:00
|
|
|
}
|
2019-06-13 09:36:07 +00:00
|
|
|
}
|
|
|
|
|
2021-04-06 20:15:24 +00:00
|
|
|
QString AbstractWaylandOutput::eisaId() const
|
|
|
|
{
|
|
|
|
return m_eisaId;
|
|
|
|
}
|
|
|
|
|
2020-11-24 17:31:06 +00:00
|
|
|
QString AbstractWaylandOutput::manufacturer() const
|
|
|
|
{
|
2021-04-04 14:11:13 +00:00
|
|
|
return m_manufacturer;
|
2020-11-24 17:31:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QString AbstractWaylandOutput::model() const
|
|
|
|
{
|
2021-04-04 14:11:13 +00:00
|
|
|
return m_model;
|
2020-11-24 17:31:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QString AbstractWaylandOutput::serialNumber() const
|
|
|
|
{
|
2021-04-04 14:11:13 +00:00
|
|
|
return m_serialNumber;
|
2020-11-24 17:31:06 +00:00
|
|
|
}
|
|
|
|
|
2020-01-02 14:55:05 +00:00
|
|
|
QSize AbstractWaylandOutput::modeSize() const
|
|
|
|
{
|
2021-04-04 14:11:13 +00:00
|
|
|
return m_modeSize;
|
2020-01-02 14:55:05 +00:00
|
|
|
}
|
|
|
|
|
2019-08-27 14:19:47 +00:00
|
|
|
QSize AbstractWaylandOutput::pixelSize() const
|
|
|
|
{
|
2021-04-04 14:11:13 +00:00
|
|
|
return orientateSize(m_modeSize);
|
2019-08-27 14:19:47 +00:00
|
|
|
}
|
|
|
|
|
2021-04-04 14:11:13 +00:00
|
|
|
QByteArray AbstractWaylandOutput::edid() const
|
2019-08-27 12:41:16 +00:00
|
|
|
{
|
2021-04-04 14:11:13 +00:00
|
|
|
return m_edid;
|
2019-08-27 12:41:16 +00:00
|
|
|
}
|
|
|
|
|
2021-04-04 14:11:13 +00:00
|
|
|
QVector<AbstractWaylandOutput::Mode> AbstractWaylandOutput::modes() const
|
2019-06-13 09:36:07 +00:00
|
|
|
{
|
2021-04-04 14:11:13 +00:00
|
|
|
return m_modes;
|
2019-06-13 09:36:07 +00:00
|
|
|
}
|
|
|
|
|
2021-04-04 14:11:13 +00:00
|
|
|
qreal AbstractWaylandOutput::scale() const
|
2019-11-26 22:53:17 +00:00
|
|
|
{
|
2021-04-04 14:11:13 +00:00
|
|
|
return m_scale;
|
2019-11-26 22:53:17 +00:00
|
|
|
}
|
|
|
|
|
2021-04-04 14:11:13 +00:00
|
|
|
void AbstractWaylandOutput::setScale(qreal scale)
|
2019-11-26 22:53:17 +00:00
|
|
|
{
|
2021-04-04 14:11:13 +00:00
|
|
|
if (m_scale != scale) {
|
|
|
|
m_scale = scale;
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT scaleChanged();
|
|
|
|
Q_EMIT geometryChanged();
|
2021-04-04 14:11:13 +00:00
|
|
|
}
|
2019-11-26 22:53:17 +00:00
|
|
|
}
|
|
|
|
|
2021-04-07 13:31:04 +00:00
|
|
|
AbstractWaylandOutput::SubPixel AbstractWaylandOutput::subPixel() const
|
|
|
|
{
|
|
|
|
return m_subPixel;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AbstractWaylandOutput::setSubPixelInternal(SubPixel subPixel)
|
|
|
|
{
|
|
|
|
m_subPixel = subPixel;
|
|
|
|
}
|
|
|
|
|
2020-04-29 15:18:41 +00:00
|
|
|
void AbstractWaylandOutput::applyChanges(const KWaylandServer::OutputChangeSet *changeSet)
|
2019-06-13 09:36:07 +00:00
|
|
|
{
|
2019-08-28 18:54:37 +00:00
|
|
|
qCDebug(KWIN_CORE) << "Apply changes to the Wayland output.";
|
2019-08-25 12:22:12 +00:00
|
|
|
bool emitModeChanged = false;
|
2020-01-02 14:55:03 +00:00
|
|
|
bool overallSizeCheckNeeded = false;
|
2019-06-13 09:36:07 +00:00
|
|
|
|
2019-08-28 18:54:37 +00:00
|
|
|
// Enablement changes are handled by platform.
|
|
|
|
if (changeSet->modeChanged()) {
|
|
|
|
qCDebug(KWIN_CORE) << "Setting new mode:" << changeSet->mode();
|
|
|
|
updateMode(changeSet->mode());
|
2019-08-25 12:22:12 +00:00
|
|
|
emitModeChanged = true;
|
2019-06-13 09:36:07 +00:00
|
|
|
}
|
2019-08-28 18:54:37 +00:00
|
|
|
if (changeSet->transformChanged()) {
|
|
|
|
qCDebug(KWIN_CORE) << "Server setting transform: " << (int)(changeSet->transform());
|
2021-04-04 14:11:13 +00:00
|
|
|
auto transform = outputDeviceTransformToKWinTransform(changeSet->transform());
|
|
|
|
setTransformInternal(transform);
|
|
|
|
updateTransform(transform);
|
2019-08-25 12:22:12 +00:00
|
|
|
emitModeChanged = true;
|
2019-06-13 09:36:07 +00:00
|
|
|
}
|
2019-08-28 18:54:37 +00:00
|
|
|
if (changeSet->positionChanged()) {
|
|
|
|
qCDebug(KWIN_CORE) << "Server setting position: " << changeSet->position();
|
|
|
|
setGlobalPos(changeSet->position());
|
2019-06-13 09:36:07 +00:00
|
|
|
// may just work already!
|
2020-01-02 14:55:03 +00:00
|
|
|
overallSizeCheckNeeded = true;
|
2019-06-13 09:36:07 +00:00
|
|
|
}
|
2019-08-28 18:54:37 +00:00
|
|
|
if (changeSet->scaleChanged()) {
|
2020-03-15 19:59:29 +00:00
|
|
|
qCDebug(KWIN_CORE) << "Setting scale:" << changeSet->scaleF();
|
2019-08-28 18:54:37 +00:00
|
|
|
setScale(changeSet->scaleF());
|
2019-08-25 12:22:12 +00:00
|
|
|
emitModeChanged = true;
|
|
|
|
}
|
2021-04-11 14:26:21 +00:00
|
|
|
if (changeSet->overscanChanged()) {
|
|
|
|
qCDebug(KWIN_CORE) << "Setting overscan:" << changeSet->overscan();
|
|
|
|
setOverscan(changeSet->overscan());
|
|
|
|
}
|
2021-02-20 15:04:18 +00:00
|
|
|
if (changeSet->vrrPolicyChanged()) {
|
|
|
|
qCDebug(KWIN_CORE) << "Setting VRR Policy:" << changeSet->vrrPolicy();
|
|
|
|
setVrrPolicy(static_cast<RenderLoop::VrrPolicy>(changeSet->vrrPolicy()));
|
|
|
|
}
|
2019-08-25 12:22:12 +00:00
|
|
|
|
2020-01-02 14:55:03 +00:00
|
|
|
overallSizeCheckNeeded |= emitModeChanged;
|
|
|
|
if (overallSizeCheckNeeded) {
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT screens()->changed();
|
2020-01-02 14:55:03 +00:00
|
|
|
}
|
|
|
|
|
2019-08-25 12:22:12 +00:00
|
|
|
if (emitModeChanged) {
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT modeChanged();
|
2019-06-13 09:36:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-28 18:54:37 +00:00
|
|
|
bool AbstractWaylandOutput::isEnabled() const
|
|
|
|
{
|
2021-04-04 14:11:13 +00:00
|
|
|
return m_isEnabled;
|
2019-08-28 18:54:37 +00:00
|
|
|
}
|
|
|
|
|
2019-06-13 09:36:07 +00:00
|
|
|
void AbstractWaylandOutput::setEnabled(bool enable)
|
|
|
|
{
|
2021-04-04 14:11:13 +00:00
|
|
|
if (m_isEnabled != enable) {
|
|
|
|
m_isEnabled = enable;
|
|
|
|
updateEnablement(enable);
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT enabledChanged();
|
2019-06-13 09:36:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-08 09:38:41 +00:00
|
|
|
QString AbstractWaylandOutput::description() const
|
|
|
|
{
|
2021-04-04 14:11:13 +00:00
|
|
|
return m_manufacturer + ' ' + m_model;
|
2020-04-08 09:38:41 +00:00
|
|
|
}
|
|
|
|
|
2021-04-04 14:11:13 +00:00
|
|
|
void AbstractWaylandOutput::setCurrentModeInternal(const QSize &size, int refreshRate)
|
2019-06-13 09:36:07 +00:00
|
|
|
{
|
2021-04-04 14:11:13 +00:00
|
|
|
if (m_modeSize != size || m_refreshRate != refreshRate) {
|
|
|
|
m_modeSize = size;
|
|
|
|
m_refreshRate = refreshRate;
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT geometryChanged();
|
2021-04-04 14:11:13 +00:00
|
|
|
}
|
2019-06-13 09:36:07 +00:00
|
|
|
}
|
|
|
|
|
2021-04-06 20:08:02 +00:00
|
|
|
static QUuid generateOutputId(const QString &eisaId, const QString &model,
|
|
|
|
const QString &serialNumber, const QString &name)
|
|
|
|
{
|
|
|
|
static const QUuid urlNs = QUuid("6ba7b811-9dad-11d1-80b4-00c04fd430c8"); // NameSpace_URL
|
|
|
|
static const QUuid kwinNs = QUuid::createUuidV5(urlNs, QStringLiteral("https://kwin.kde.org/o/"));
|
|
|
|
|
|
|
|
const QString payload = QStringList{name, eisaId, model, serialNumber}.join(':');
|
|
|
|
return QUuid::createUuidV5(kwinNs, payload);
|
|
|
|
}
|
|
|
|
|
2021-04-04 14:11:13 +00:00
|
|
|
void AbstractWaylandOutput::initialize(const QString &model, const QString &manufacturer,
|
2021-04-06 20:15:24 +00:00
|
|
|
const QString &eisaId, const QString &serialNumber,
|
2021-04-06 20:08:02 +00:00
|
|
|
const QSize &physicalSize,
|
2021-04-04 14:11:13 +00:00
|
|
|
const QVector<Mode> &modes, const QByteArray &edid)
|
2019-06-13 09:36:07 +00:00
|
|
|
{
|
2021-04-06 20:15:24 +00:00
|
|
|
m_serialNumber = serialNumber;
|
|
|
|
m_eisaId = eisaId;
|
2021-04-04 14:11:13 +00:00
|
|
|
m_manufacturer = manufacturer.isEmpty() ? i18n("unknown") : manufacturer;
|
|
|
|
m_model = model;
|
|
|
|
m_physicalSize = physicalSize;
|
|
|
|
m_edid = edid;
|
|
|
|
m_modes = modes;
|
2021-04-06 20:08:02 +00:00
|
|
|
m_uuid = generateOutputId(m_eisaId, m_model, m_serialNumber, m_name);
|
2021-04-04 14:11:13 +00:00
|
|
|
|
|
|
|
for (const Mode &mode : modes) {
|
|
|
|
if (mode.flags & ModeFlag::Current) {
|
|
|
|
m_modeSize = mode.size;
|
|
|
|
m_refreshRate = mode.refreshRate;
|
|
|
|
break;
|
2020-03-23 23:04:06 +00:00
|
|
|
}
|
2019-06-13 09:36:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QSize AbstractWaylandOutput::orientateSize(const QSize &size) const
|
|
|
|
{
|
2021-04-04 14:11:13 +00:00
|
|
|
if (m_transform == Transform::Rotated90 || m_transform == Transform::Rotated270 ||
|
|
|
|
m_transform == Transform::Flipped90 || m_transform == Transform::Flipped270) {
|
2019-06-13 09:36:07 +00:00
|
|
|
return size.transposed();
|
|
|
|
}
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2021-04-04 14:11:13 +00:00
|
|
|
void AbstractWaylandOutput::setTransformInternal(Transform transform)
|
2019-11-24 15:51:59 +00:00
|
|
|
{
|
2021-04-04 14:11:13 +00:00
|
|
|
if (m_transform != transform) {
|
|
|
|
m_transform = transform;
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT transformChanged();
|
|
|
|
Q_EMIT modeChanged();
|
2019-11-24 15:51:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-26 22:53:17 +00:00
|
|
|
AbstractWaylandOutput::Transform AbstractWaylandOutput::transform() const
|
2019-11-24 11:26:18 +00:00
|
|
|
{
|
2021-04-04 14:11:13 +00:00
|
|
|
return m_transform;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AbstractWaylandOutput::setDpmsModeInternal(DpmsMode dpmsMode)
|
|
|
|
{
|
|
|
|
if (m_dpmsMode != dpmsMode) {
|
|
|
|
m_dpmsMode = dpmsMode;
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT dpmsModeChanged();
|
2021-04-04 14:11:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AbstractWaylandOutput::setDpmsMode(DpmsMode mode)
|
|
|
|
{
|
|
|
|
Q_UNUSED(mode)
|
|
|
|
}
|
|
|
|
|
|
|
|
AbstractWaylandOutput::DpmsMode AbstractWaylandOutput::dpmsMode() const
|
|
|
|
{
|
|
|
|
return m_dpmsMode;
|
2019-11-24 11:26:18 +00:00
|
|
|
}
|
|
|
|
|
2020-10-13 20:57:15 +00:00
|
|
|
QMatrix4x4 AbstractWaylandOutput::logicalToNativeMatrix(const QRect &rect, qreal scale, Transform transform)
|
2020-08-12 16:52:08 +00:00
|
|
|
{
|
|
|
|
QMatrix4x4 matrix;
|
2020-10-13 20:57:15 +00:00
|
|
|
matrix.scale(scale);
|
2020-08-12 16:52:08 +00:00
|
|
|
|
2020-10-13 20:57:15 +00:00
|
|
|
switch (transform) {
|
2020-10-13 20:15:46 +00:00
|
|
|
case Transform::Normal:
|
|
|
|
case Transform::Flipped:
|
|
|
|
break;
|
|
|
|
case Transform::Rotated90:
|
|
|
|
case Transform::Flipped90:
|
|
|
|
matrix.translate(0, rect.width());
|
|
|
|
matrix.rotate(-90, 0, 0, 1);
|
|
|
|
break;
|
|
|
|
case Transform::Rotated180:
|
|
|
|
case Transform::Flipped180:
|
|
|
|
matrix.translate(rect.width(), rect.height());
|
|
|
|
matrix.rotate(-180, 0, 0, 1);
|
|
|
|
break;
|
|
|
|
case Transform::Rotated270:
|
|
|
|
case Transform::Flipped270:
|
|
|
|
matrix.translate(rect.height(), 0);
|
|
|
|
matrix.rotate(-270, 0, 0, 1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-10-13 20:57:15 +00:00
|
|
|
switch (transform) {
|
2020-10-13 20:15:46 +00:00
|
|
|
case Transform::Flipped:
|
|
|
|
case Transform::Flipped90:
|
|
|
|
case Transform::Flipped180:
|
|
|
|
case Transform::Flipped270:
|
|
|
|
matrix.translate(rect.width(), 0);
|
|
|
|
matrix.scale(-1, 1);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
matrix.translate(-rect.x(), -rect.y());
|
|
|
|
|
2020-08-12 16:52:08 +00:00
|
|
|
return matrix;
|
|
|
|
}
|
|
|
|
|
2021-02-02 13:26:43 +00:00
|
|
|
void AbstractWaylandOutput::recordingStarted()
|
|
|
|
{
|
|
|
|
m_recorders++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AbstractWaylandOutput::recordingStopped()
|
|
|
|
{
|
|
|
|
m_recorders--;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool AbstractWaylandOutput::isBeingRecorded()
|
|
|
|
{
|
|
|
|
return m_recorders;
|
|
|
|
}
|
|
|
|
|
2021-04-11 14:26:21 +00:00
|
|
|
void AbstractWaylandOutput::setOverscanInternal(uint32_t overscan)
|
|
|
|
{
|
|
|
|
if (m_overscan != overscan) {
|
|
|
|
m_overscan = overscan;
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT overscanChanged();
|
2021-04-11 14:26:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t AbstractWaylandOutput::overscan() const
|
|
|
|
{
|
|
|
|
return m_overscan;
|
|
|
|
}
|
|
|
|
|
|
|
|
void AbstractWaylandOutput::setOverscan(uint32_t overscan)
|
|
|
|
{
|
|
|
|
Q_UNUSED(overscan);
|
|
|
|
}
|
|
|
|
|
2021-02-20 15:04:18 +00:00
|
|
|
void AbstractWaylandOutput::setVrrPolicy(RenderLoop::VrrPolicy policy)
|
|
|
|
{
|
2021-05-01 21:22:38 +00:00
|
|
|
if (renderLoop()->vrrPolicy() != policy && (m_capabilities & Capability::Vrr)) {
|
2021-02-20 15:04:18 +00:00
|
|
|
renderLoop()->setVrrPolicy(policy);
|
2021-06-08 07:02:14 +00:00
|
|
|
Q_EMIT vrrPolicyChanged();
|
2021-02-20 15:04:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
RenderLoop::VrrPolicy AbstractWaylandOutput::vrrPolicy() const
|
|
|
|
{
|
|
|
|
return renderLoop()->vrrPolicy();
|
|
|
|
}
|
|
|
|
|
2019-06-13 09:36:07 +00:00
|
|
|
}
|