[platforms/x11/standalone] Port to AbstractOutput
Summary: Represent outputs in the X11 session via AbstractOutput. For that we move all Wayland specific parts of AbstractOutput into a new subclass AbstractWaylandOutput and let the outputs of our Wayland backends inherit from there. This should allow us to get rid of the Screens class later on. Test Plan: Manually in X session. Reviewers: #kwin, zzag, davidedmundson Reviewed By: #kwin, zzag, davidedmundson Subscribers: ngraham, zzag, kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D19208
This commit is contained in:
parent
8040c559cb
commit
1a11abc821
25 changed files with 746 additions and 861 deletions
|
@ -470,6 +470,7 @@ set(kwin_KDEINIT_SRCS
|
|||
decorations/decorations_logging.cpp
|
||||
platform.cpp
|
||||
abstract_output.cpp
|
||||
abstract_wayland_output.cpp
|
||||
shell_client.cpp
|
||||
wayland_server.cpp
|
||||
wayland_cursor_theme.cpp
|
||||
|
|
|
@ -18,12 +18,7 @@ You should have received a copy of the GNU General Public License
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#include "abstract_output.h"
|
||||
#include "wayland_server.h"
|
||||
|
||||
// KWayland
|
||||
#include <KWayland/Server/display.h>
|
||||
#include <KWayland/Server/outputchangeset.h>
|
||||
#include <KWayland/Server/xdgoutput_interface.h>
|
||||
// KF5
|
||||
#include <KLocalizedString>
|
||||
|
||||
|
@ -39,224 +34,6 @@ AbstractOutput::AbstractOutput(QObject *parent)
|
|||
|
||||
AbstractOutput::~AbstractOutput()
|
||||
{
|
||||
delete m_waylandOutputDevice.data();
|
||||
delete m_xdgOutput.data();
|
||||
delete m_waylandOutput.data();
|
||||
}
|
||||
|
||||
QString AbstractOutput::name() const
|
||||
{
|
||||
if (!m_waylandOutput) {
|
||||
return i18n("unknown");
|
||||
}
|
||||
return QStringLiteral("%1 %2").arg(m_waylandOutput->manufacturer()).arg(m_waylandOutput->model());
|
||||
}
|
||||
|
||||
QRect AbstractOutput::geometry() const
|
||||
{
|
||||
return QRect(m_globalPos, pixelSize() / scale());
|
||||
}
|
||||
|
||||
QSize AbstractOutput::physicalSize() const
|
||||
{
|
||||
return orientateSize(m_physicalSize);
|
||||
}
|
||||
|
||||
int AbstractOutput::refreshRate() const
|
||||
{
|
||||
if (!m_waylandOutput) {
|
||||
return 60000;
|
||||
}
|
||||
return m_waylandOutput->refreshRate();
|
||||
}
|
||||
|
||||
void AbstractOutput::setGlobalPos(const QPoint &pos)
|
||||
{
|
||||
m_globalPos = pos;
|
||||
if (m_waylandOutput) {
|
||||
m_waylandOutput->setGlobalPosition(pos);
|
||||
}
|
||||
if (m_waylandOutputDevice) {
|
||||
m_waylandOutputDevice->setGlobalPosition(pos);
|
||||
}
|
||||
if (m_xdgOutput) {
|
||||
m_xdgOutput->setLogicalPosition(pos);
|
||||
m_xdgOutput->done();
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractOutput::setScale(qreal scale)
|
||||
{
|
||||
m_scale = scale;
|
||||
if (m_waylandOutput) {
|
||||
// this is the scale that clients will ideally use for their buffers
|
||||
// this has to be an int which is fine
|
||||
|
||||
// I don't know whether we want to round or ceil
|
||||
// or maybe even set this to 3 when we're scaling to 1.5
|
||||
// don't treat this like it's chosen deliberately
|
||||
m_waylandOutput->setScale(std::ceil(scale));
|
||||
}
|
||||
if (m_waylandOutputDevice) {
|
||||
m_waylandOutputDevice->setScaleF(scale);
|
||||
}
|
||||
if (m_xdgOutput) {
|
||||
m_xdgOutput->setLogicalSize(pixelSize() / m_scale);
|
||||
m_xdgOutput->done();
|
||||
}
|
||||
emit modeChanged();
|
||||
}
|
||||
|
||||
void AbstractOutput::setChanges(KWayland::Server::OutputChangeSet *changes)
|
||||
{
|
||||
qCDebug(KWIN_CORE) << "Set changes in AbstractOutput.";
|
||||
Q_ASSERT(!m_waylandOutputDevice.isNull());
|
||||
|
||||
if (!changes) {
|
||||
qCDebug(KWIN_CORE) << "No changes.";
|
||||
// No changes to an output is an entirely valid thing
|
||||
}
|
||||
//enabledChanged is handled by plugin code
|
||||
if (changes->modeChanged()) {
|
||||
qCDebug(KWIN_CORE) << "Setting new mode:" << changes->mode();
|
||||
m_waylandOutputDevice->setCurrentMode(changes->mode());
|
||||
updateMode(changes->mode());
|
||||
}
|
||||
if (changes->transformChanged()) {
|
||||
qCDebug(KWIN_CORE) << "Server setting transform: " << (int)(changes->transform());
|
||||
transform(changes->transform());
|
||||
}
|
||||
if (changes->positionChanged()) {
|
||||
qCDebug(KWIN_CORE) << "Server setting position: " << changes->position();
|
||||
setGlobalPos(changes->position());
|
||||
// may just work already!
|
||||
}
|
||||
if (changes->scaleChanged()) {
|
||||
qCDebug(KWIN_CORE) << "Setting scale:" << changes->scale();
|
||||
setScale(changes->scaleF());
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractOutput::setEnabled(bool enable)
|
||||
{
|
||||
if (enable == isEnabled()) {
|
||||
return;
|
||||
}
|
||||
if (enable) {
|
||||
updateDpms(KWayland::Server::OutputInterface::DpmsMode::On);
|
||||
initWaylandOutput();
|
||||
} else {
|
||||
updateDpms(KWayland::Server::OutputInterface::DpmsMode::Off);
|
||||
delete waylandOutput().data();
|
||||
}
|
||||
waylandOutputDevice()->setEnabled(enable ? KWayland::Server::OutputDeviceInterface::Enablement::Enabled :
|
||||
KWayland::Server::OutputDeviceInterface::Enablement::Disabled);
|
||||
}
|
||||
|
||||
void AbstractOutput::setWaylandMode(const QSize &size, int refreshRate)
|
||||
{
|
||||
if (m_waylandOutput.isNull()) {
|
||||
return;
|
||||
}
|
||||
m_waylandOutput->setCurrentMode(size, refreshRate);
|
||||
if (m_xdgOutput) {
|
||||
m_xdgOutput->setLogicalSize(pixelSize() / scale());
|
||||
m_xdgOutput->done();
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractOutput::createXdgOutput()
|
||||
{
|
||||
if (!m_waylandOutput || m_xdgOutput) {
|
||||
return;
|
||||
}
|
||||
m_xdgOutput = waylandServer()->xdgOutputManager()->createXdgOutput(m_waylandOutput, m_waylandOutput);
|
||||
m_xdgOutput->setLogicalSize(pixelSize() / scale());
|
||||
m_xdgOutput->setLogicalPosition(m_globalPos);
|
||||
m_xdgOutput->done();
|
||||
}
|
||||
|
||||
void AbstractOutput::initWaylandOutput()
|
||||
{
|
||||
Q_ASSERT(m_waylandOutputDevice);
|
||||
|
||||
if (!m_waylandOutput.isNull()) {
|
||||
delete m_waylandOutput.data();
|
||||
m_waylandOutput.clear();
|
||||
}
|
||||
m_waylandOutput = waylandServer()->display()->createOutput();
|
||||
createXdgOutput();
|
||||
|
||||
/*
|
||||
* add base wayland output data
|
||||
*/
|
||||
m_waylandOutput->setManufacturer(m_waylandOutputDevice->manufacturer());
|
||||
m_waylandOutput->setModel(m_waylandOutputDevice->model());
|
||||
m_waylandOutput->setPhysicalSize(rawPhysicalSize());
|
||||
|
||||
/*
|
||||
* add modes
|
||||
*/
|
||||
for(const auto &mode: m_waylandOutputDevice->modes()) {
|
||||
KWayland::Server::OutputInterface::ModeFlags flags;
|
||||
if (mode.flags & KWayland::Server::OutputDeviceInterface::ModeFlag::Current) {
|
||||
flags |= KWayland::Server::OutputInterface::ModeFlag::Current;
|
||||
}
|
||||
if (mode.flags & KWayland::Server::OutputDeviceInterface::ModeFlag::Preferred) {
|
||||
flags |= KWayland::Server::OutputInterface::ModeFlag::Preferred;
|
||||
}
|
||||
m_waylandOutput->addMode(mode.size, flags, mode.refreshRate);
|
||||
}
|
||||
m_waylandOutput->create();
|
||||
|
||||
/*
|
||||
* set dpms
|
||||
*/
|
||||
m_waylandOutput->setDpmsSupported(m_supportsDpms);
|
||||
// set to last known mode
|
||||
m_waylandOutput->setDpmsMode(m_dpms);
|
||||
connect(m_waylandOutput.data(), &KWayland::Server::OutputInterface::dpmsModeRequested, this,
|
||||
[this] (KWayland::Server::OutputInterface::DpmsMode mode) {
|
||||
updateDpms(mode);
|
||||
}, Qt::QueuedConnection
|
||||
);
|
||||
}
|
||||
|
||||
void AbstractOutput::initWaylandOutputDevice(const QString &model,
|
||||
const QString &manufacturer,
|
||||
const QByteArray &uuid,
|
||||
const QVector<KWayland::Server::OutputDeviceInterface::Mode> &modes)
|
||||
{
|
||||
if (!m_waylandOutputDevice.isNull()) {
|
||||
delete m_waylandOutputDevice.data();
|
||||
m_waylandOutputDevice.clear();
|
||||
}
|
||||
m_waylandOutputDevice = waylandServer()->display()->createOutputDevice();
|
||||
m_waylandOutputDevice->setUuid(uuid);
|
||||
|
||||
if (!manufacturer.isEmpty()) {
|
||||
m_waylandOutputDevice->setManufacturer(manufacturer);
|
||||
} else {
|
||||
m_waylandOutputDevice->setManufacturer(i18n("unknown"));
|
||||
}
|
||||
|
||||
m_waylandOutputDevice->setModel(model);
|
||||
m_waylandOutputDevice->setPhysicalSize(m_physicalSize);
|
||||
|
||||
int i = 0;
|
||||
for (auto mode : modes) {
|
||||
qCDebug(KWIN_CORE).nospace() << "Adding mode " << ++i << ": " << mode.size << " [" << mode.refreshRate << "]";
|
||||
m_waylandOutputDevice->addMode(mode);
|
||||
}
|
||||
m_waylandOutputDevice->create();
|
||||
}
|
||||
|
||||
QSize AbstractOutput::orientateSize(const QSize &size) const
|
||||
{
|
||||
if (m_orientation == Qt::PortraitOrientation || m_orientation == Qt::InvertedPortraitOrientation) {
|
||||
return size.transposed();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright 2018 Roman Gilg <subdiff@gmail.com>
|
||||
Copyright 2019 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
|
@ -17,33 +17,14 @@ GNU General Public License for more details.
|
|||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#ifndef KWIN_OUTPUT_H
|
||||
#define KWIN_OUTPUT_H
|
||||
#ifndef KWIN_ABSTRACT_OUTPUT_H
|
||||
#define KWIN_ABSTRACT_OUTPUT_H
|
||||
|
||||
#include <utils.h>
|
||||
#include <kwin_export.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QPoint>
|
||||
#include <QPointer>
|
||||
#include <QRect>
|
||||
#include <QSize>
|
||||
#include <QVector>
|
||||
|
||||
#include <KWayland/Server/output_interface.h>
|
||||
#include <KWayland/Server/outputdevice_interface.h>
|
||||
|
||||
namespace KWayland
|
||||
{
|
||||
namespace Server
|
||||
{
|
||||
class OutputInterface;
|
||||
class OutputDeviceInterface;
|
||||
class OutputChangeSet;
|
||||
class OutputManagementInterface;
|
||||
class XdgOutputInterface;
|
||||
}
|
||||
}
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
@ -62,52 +43,26 @@ public:
|
|||
explicit AbstractOutput(QObject *parent = nullptr);
|
||||
virtual ~AbstractOutput();
|
||||
|
||||
QString name() const;
|
||||
bool isEnabled() const {
|
||||
return !m_waylandOutput.isNull();
|
||||
}
|
||||
|
||||
virtual QSize pixelSize() const = 0;
|
||||
qreal scale() const {
|
||||
return m_scale;
|
||||
}
|
||||
/**
|
||||
* The geometry of this output in global compositor co-ordinates (i.e scaled)
|
||||
**/
|
||||
QRect geometry() const;
|
||||
QSize physicalSize() const;
|
||||
Qt::ScreenOrientation orientation() const {
|
||||
return m_orientation;
|
||||
}
|
||||
virtual QString name() const = 0;
|
||||
virtual QRect geometry() const = 0;
|
||||
|
||||
/**
|
||||
* Current refresh rate in 1/ms.
|
||||
**/
|
||||
int refreshRate() const;
|
||||
virtual int refreshRate() const = 0;
|
||||
|
||||
bool isInternal() const {
|
||||
return m_internal;
|
||||
virtual bool isInternal() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void setGlobalPos(const QPoint &pos);
|
||||
void setScale(qreal scale);
|
||||
|
||||
/**
|
||||
* This sets the changes and tests them against the specific output.
|
||||
**/
|
||||
void setChanges(KWayland::Server::OutputChangeSet *changeset);
|
||||
|
||||
QPointer<KWayland::Server::OutputInterface> waylandOutput() const {
|
||||
return m_waylandOutput;
|
||||
virtual qreal scale() const {
|
||||
return 1.;
|
||||
}
|
||||
virtual QSize physicalSize() const {
|
||||
return QSize();
|
||||
}
|
||||
virtual Qt::ScreenOrientation orientation() const {
|
||||
return Qt::PrimaryOrientation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable the output.
|
||||
*
|
||||
* This differs from updateDpms as it also removes the wl_output.
|
||||
* The default is on.
|
||||
**/
|
||||
void setEnabled(bool enable);
|
||||
|
||||
virtual int getGammaRampSize() const {
|
||||
return 0;
|
||||
|
@ -116,75 +71,8 @@ public:
|
|||
Q_UNUSED(gamma);
|
||||
return false;
|
||||
}
|
||||
|
||||
Q_SIGNALS:
|
||||
void modeChanged();
|
||||
|
||||
protected:
|
||||
void initWaylandOutput();
|
||||
void initWaylandOutputDevice(const QString &model,
|
||||
const QString &manufacturer,
|
||||
const QByteArray &uuid,
|
||||
const QVector<KWayland::Server::OutputDeviceInterface::Mode> &modes);
|
||||
|
||||
QPointer<KWayland::Server::XdgOutputInterface> xdgOutput() const {
|
||||
return m_xdgOutput;
|
||||
}
|
||||
void createXdgOutput();
|
||||
|
||||
QPointer<KWayland::Server::OutputDeviceInterface> waylandOutputDevice() const {
|
||||
return m_waylandOutputDevice;
|
||||
}
|
||||
|
||||
QPoint globalPos() const {
|
||||
return m_globalPos;
|
||||
}
|
||||
|
||||
QSize rawPhysicalSize() const {
|
||||
return m_physicalSize;
|
||||
}
|
||||
void setRawPhysicalSize(const QSize &set) {
|
||||
m_physicalSize = set;
|
||||
}
|
||||
|
||||
void setOrientation(Qt::ScreenOrientation set) {
|
||||
m_orientation = set;
|
||||
}
|
||||
void setInternal(bool set) {
|
||||
m_internal = set;
|
||||
}
|
||||
void setDpmsSupported(bool set) {
|
||||
m_supportsDpms = set;
|
||||
}
|
||||
virtual void updateDpms(KWayland::Server::OutputInterface::DpmsMode mode) {
|
||||
Q_UNUSED(mode);
|
||||
}
|
||||
virtual void updateMode(int modeIndex) {
|
||||
Q_UNUSED(modeIndex);
|
||||
}
|
||||
virtual void transform(KWayland::Server::OutputDeviceInterface::Transform transform) {
|
||||
Q_UNUSED(transform);
|
||||
}
|
||||
|
||||
void setWaylandMode(const QSize &size, int refreshRate);
|
||||
|
||||
QSize orientateSize(const QSize &size) const;
|
||||
|
||||
private:
|
||||
QPointer<KWayland::Server::OutputInterface> m_waylandOutput;
|
||||
QPointer<KWayland::Server::XdgOutputInterface> m_xdgOutput;
|
||||
QPointer<KWayland::Server::OutputDeviceInterface> m_waylandOutputDevice;
|
||||
|
||||
KWayland::Server::OutputInterface::DpmsMode m_dpms = KWayland::Server::OutputInterface::DpmsMode::On;
|
||||
|
||||
QPoint m_globalPos;
|
||||
qreal m_scale = 1;
|
||||
QSize m_physicalSize;
|
||||
Qt::ScreenOrientation m_orientation = Qt::PrimaryOrientation;
|
||||
bool m_internal = false;
|
||||
bool m_supportsDpms = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // KWIN_OUTPUT_H
|
||||
#endif
|
||||
|
|
259
abstract_wayland_output.cpp
Normal file
259
abstract_wayland_output.cpp
Normal file
|
@ -0,0 +1,259 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright 2019 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#include "abstract_wayland_output.h"
|
||||
#include "wayland_server.h"
|
||||
|
||||
// KWayland
|
||||
#include <KWayland/Server/display.h>
|
||||
#include <KWayland/Server/outputchangeset.h>
|
||||
#include <KWayland/Server/xdgoutput_interface.h>
|
||||
// KF5
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
AbstractWaylandOutput::AbstractWaylandOutput(QObject *parent)
|
||||
: AbstractOutput(parent)
|
||||
{
|
||||
}
|
||||
|
||||
AbstractWaylandOutput::~AbstractWaylandOutput()
|
||||
{
|
||||
delete m_waylandOutputDevice.data();
|
||||
delete m_xdgOutput.data();
|
||||
delete m_waylandOutput.data();
|
||||
}
|
||||
|
||||
QString AbstractWaylandOutput::name() const
|
||||
{
|
||||
if (!m_waylandOutput) {
|
||||
return i18n("unknown");
|
||||
}
|
||||
return QStringLiteral("%1 %2").arg(m_waylandOutput->manufacturer()).arg(m_waylandOutput->model());
|
||||
}
|
||||
|
||||
QRect AbstractWaylandOutput::geometry() const
|
||||
{
|
||||
return QRect(m_globalPos, pixelSize() / scale());
|
||||
}
|
||||
|
||||
QSize AbstractWaylandOutput::physicalSize() const
|
||||
{
|
||||
return orientateSize(m_physicalSize);
|
||||
}
|
||||
|
||||
int AbstractWaylandOutput::refreshRate() const
|
||||
{
|
||||
if (!m_waylandOutput) {
|
||||
return 60000;
|
||||
}
|
||||
return m_waylandOutput->refreshRate();
|
||||
}
|
||||
|
||||
void AbstractWaylandOutput::setGlobalPos(const QPoint &pos)
|
||||
{
|
||||
m_globalPos = pos;
|
||||
if (m_waylandOutput) {
|
||||
m_waylandOutput->setGlobalPosition(pos);
|
||||
}
|
||||
if (m_waylandOutputDevice) {
|
||||
m_waylandOutputDevice->setGlobalPosition(pos);
|
||||
}
|
||||
if (m_xdgOutput) {
|
||||
m_xdgOutput->setLogicalPosition(pos);
|
||||
m_xdgOutput->done();
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractWaylandOutput::setScale(qreal scale)
|
||||
{
|
||||
m_scale = scale;
|
||||
if (m_waylandOutput) {
|
||||
// this is the scale that clients will ideally use for their buffers
|
||||
// this has to be an int which is fine
|
||||
|
||||
// I don't know whether we want to round or ceil
|
||||
// or maybe even set this to 3 when we're scaling to 1.5
|
||||
// don't treat this like it's chosen deliberately
|
||||
m_waylandOutput->setScale(std::ceil(scale));
|
||||
}
|
||||
if (m_waylandOutputDevice) {
|
||||
m_waylandOutputDevice->setScaleF(scale);
|
||||
}
|
||||
if (m_xdgOutput) {
|
||||
m_xdgOutput->setLogicalSize(pixelSize() / m_scale);
|
||||
m_xdgOutput->done();
|
||||
}
|
||||
emit modeChanged();
|
||||
}
|
||||
|
||||
void AbstractWaylandOutput::setChanges(KWayland::Server::OutputChangeSet *changes)
|
||||
{
|
||||
qCDebug(KWIN_CORE) << "Set changes in AbstractWaylandOutput.";
|
||||
Q_ASSERT(!m_waylandOutputDevice.isNull());
|
||||
|
||||
if (!changes) {
|
||||
qCDebug(KWIN_CORE) << "No changes.";
|
||||
// No changes to an output is an entirely valid thing
|
||||
}
|
||||
//enabledChanged is handled by plugin code
|
||||
if (changes->modeChanged()) {
|
||||
qCDebug(KWIN_CORE) << "Setting new mode:" << changes->mode();
|
||||
m_waylandOutputDevice->setCurrentMode(changes->mode());
|
||||
updateMode(changes->mode());
|
||||
}
|
||||
if (changes->transformChanged()) {
|
||||
qCDebug(KWIN_CORE) << "Server setting transform: " << (int)(changes->transform());
|
||||
transform(changes->transform());
|
||||
}
|
||||
if (changes->positionChanged()) {
|
||||
qCDebug(KWIN_CORE) << "Server setting position: " << changes->position();
|
||||
setGlobalPos(changes->position());
|
||||
// may just work already!
|
||||
}
|
||||
if (changes->scaleChanged()) {
|
||||
qCDebug(KWIN_CORE) << "Setting scale:" << changes->scale();
|
||||
setScale(changes->scaleF());
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractWaylandOutput::setEnabled(bool enable)
|
||||
{
|
||||
if (enable == isEnabled()) {
|
||||
return;
|
||||
}
|
||||
if (enable) {
|
||||
updateDpms(KWayland::Server::OutputInterface::DpmsMode::On);
|
||||
initWaylandOutput();
|
||||
} else {
|
||||
updateDpms(KWayland::Server::OutputInterface::DpmsMode::Off);
|
||||
delete waylandOutput().data();
|
||||
}
|
||||
waylandOutputDevice()->setEnabled(enable ? KWayland::Server::OutputDeviceInterface::Enablement::Enabled :
|
||||
KWayland::Server::OutputDeviceInterface::Enablement::Disabled);
|
||||
}
|
||||
|
||||
void AbstractWaylandOutput::setWaylandMode(const QSize &size, int refreshRate)
|
||||
{
|
||||
if (m_waylandOutput.isNull()) {
|
||||
return;
|
||||
}
|
||||
m_waylandOutput->setCurrentMode(size, refreshRate);
|
||||
if (m_xdgOutput) {
|
||||
m_xdgOutput->setLogicalSize(pixelSize() / scale());
|
||||
m_xdgOutput->done();
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractWaylandOutput::createXdgOutput()
|
||||
{
|
||||
if (!m_waylandOutput || m_xdgOutput) {
|
||||
return;
|
||||
}
|
||||
m_xdgOutput = waylandServer()->xdgOutputManager()->createXdgOutput(m_waylandOutput, m_waylandOutput);
|
||||
}
|
||||
|
||||
void AbstractWaylandOutput::initWaylandOutput()
|
||||
{
|
||||
Q_ASSERT(m_waylandOutputDevice);
|
||||
|
||||
if (!m_waylandOutput.isNull()) {
|
||||
delete m_waylandOutput.data();
|
||||
m_waylandOutput.clear();
|
||||
}
|
||||
m_waylandOutput = waylandServer()->display()->createOutput();
|
||||
createXdgOutput();
|
||||
|
||||
/*
|
||||
* add base wayland output data
|
||||
*/
|
||||
m_waylandOutput->setManufacturer(m_waylandOutputDevice->manufacturer());
|
||||
m_waylandOutput->setModel(m_waylandOutputDevice->model());
|
||||
m_waylandOutput->setPhysicalSize(rawPhysicalSize());
|
||||
|
||||
/*
|
||||
* add modes
|
||||
*/
|
||||
for(const auto &mode: m_waylandOutputDevice->modes()) {
|
||||
KWayland::Server::OutputInterface::ModeFlags flags;
|
||||
if (mode.flags & KWayland::Server::OutputDeviceInterface::ModeFlag::Current) {
|
||||
flags |= KWayland::Server::OutputInterface::ModeFlag::Current;
|
||||
}
|
||||
if (mode.flags & KWayland::Server::OutputDeviceInterface::ModeFlag::Preferred) {
|
||||
flags |= KWayland::Server::OutputInterface::ModeFlag::Preferred;
|
||||
}
|
||||
m_waylandOutput->addMode(mode.size, flags, mode.refreshRate);
|
||||
}
|
||||
m_waylandOutput->create();
|
||||
|
||||
/*
|
||||
* set dpms
|
||||
*/
|
||||
m_waylandOutput->setDpmsSupported(m_supportsDpms);
|
||||
// set to last known mode
|
||||
m_waylandOutput->setDpmsMode(m_dpms);
|
||||
connect(m_waylandOutput.data(), &KWayland::Server::OutputInterface::dpmsModeRequested, this,
|
||||
[this] (KWayland::Server::OutputInterface::DpmsMode mode) {
|
||||
updateDpms(mode);
|
||||
}, Qt::QueuedConnection
|
||||
);
|
||||
}
|
||||
|
||||
void AbstractWaylandOutput::initWaylandOutputDevice(const QString &model,
|
||||
const QString &manufacturer,
|
||||
const QByteArray &uuid,
|
||||
const QVector<KWayland::Server::OutputDeviceInterface::Mode> &modes)
|
||||
{
|
||||
if (!m_waylandOutputDevice.isNull()) {
|
||||
delete m_waylandOutputDevice.data();
|
||||
m_waylandOutputDevice.clear();
|
||||
}
|
||||
m_waylandOutputDevice = waylandServer()->display()->createOutputDevice();
|
||||
m_waylandOutputDevice->setUuid(uuid);
|
||||
|
||||
if (!manufacturer.isEmpty()) {
|
||||
m_waylandOutputDevice->setManufacturer(manufacturer);
|
||||
} else {
|
||||
m_waylandOutputDevice->setManufacturer(i18n("unknown"));
|
||||
}
|
||||
|
||||
m_waylandOutputDevice->setModel(model);
|
||||
m_waylandOutputDevice->setPhysicalSize(m_physicalSize);
|
||||
|
||||
int i = 0;
|
||||
for (auto mode : modes) {
|
||||
qCDebug(KWIN_CORE).nospace() << "Adding mode " << ++i << ": " << mode.size << " [" << mode.refreshRate << "]";
|
||||
m_waylandOutputDevice->addMode(mode);
|
||||
}
|
||||
m_waylandOutputDevice->create();
|
||||
}
|
||||
|
||||
QSize AbstractWaylandOutput::orientateSize(const QSize &size) const
|
||||
{
|
||||
if (m_orientation == Qt::PortraitOrientation || m_orientation == Qt::InvertedPortraitOrientation) {
|
||||
return size.transposed();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
}
|
182
abstract_wayland_output.h
Normal file
182
abstract_wayland_output.h
Normal file
|
@ -0,0 +1,182 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright 2019 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#ifndef KWIN_ABSTRACT_WAYLAND_OUTPUT_H
|
||||
#define KWIN_ABSTRACT_WAYLAND_OUTPUT_H
|
||||
|
||||
#include "abstract_output.h"
|
||||
#include <utils.h>
|
||||
#include <kwin_export.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QPoint>
|
||||
#include <QPointer>
|
||||
#include <QRect>
|
||||
#include <QSize>
|
||||
#include <QVector>
|
||||
|
||||
#include <KWayland/Server/output_interface.h>
|
||||
#include <KWayland/Server/outputdevice_interface.h>
|
||||
|
||||
namespace KWayland
|
||||
{
|
||||
namespace Server
|
||||
{
|
||||
class OutputInterface;
|
||||
class OutputDeviceInterface;
|
||||
class OutputChangeSet;
|
||||
class OutputManagementInterface;
|
||||
class XdgOutputInterface;
|
||||
}
|
||||
}
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
/**
|
||||
* Generic output representation in a Wayland session
|
||||
**/
|
||||
class KWIN_EXPORT AbstractWaylandOutput : public AbstractOutput
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AbstractWaylandOutput(QObject *parent = nullptr);
|
||||
virtual ~AbstractWaylandOutput();
|
||||
|
||||
QString name() const override;
|
||||
bool isEnabled() const {
|
||||
return !m_waylandOutput.isNull();
|
||||
}
|
||||
|
||||
virtual QSize pixelSize() const = 0;
|
||||
qreal scale() const override {
|
||||
return m_scale;
|
||||
}
|
||||
/**
|
||||
* The geometry of this output in global compositor co-ordinates (i.e scaled)
|
||||
**/
|
||||
QRect geometry() const override;
|
||||
QSize physicalSize() const override;
|
||||
Qt::ScreenOrientation orientation() const override {
|
||||
return m_orientation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Current refresh rate in 1/ms.
|
||||
**/
|
||||
int refreshRate() const override;
|
||||
|
||||
bool isInternal() const override {
|
||||
return m_internal;
|
||||
}
|
||||
|
||||
void setGlobalPos(const QPoint &pos);
|
||||
void setScale(qreal scale);
|
||||
|
||||
/**
|
||||
* This sets the changes and tests them against the specific output.
|
||||
**/
|
||||
void setChanges(KWayland::Server::OutputChangeSet *changeset);
|
||||
|
||||
QPointer<KWayland::Server::OutputInterface> waylandOutput() const {
|
||||
return m_waylandOutput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable or disable the output.
|
||||
*
|
||||
* This differs from updateDpms as it also removes the wl_output.
|
||||
* The default is on.
|
||||
**/
|
||||
void setEnabled(bool enable);
|
||||
|
||||
Q_SIGNALS:
|
||||
void modeChanged();
|
||||
|
||||
protected:
|
||||
void initWaylandOutput();
|
||||
void initWaylandOutputDevice(const QString &model,
|
||||
const QString &manufacturer,
|
||||
const QByteArray &uuid,
|
||||
const QVector<KWayland::Server::OutputDeviceInterface::Mode> &modes);
|
||||
|
||||
QPointer<KWayland::Server::XdgOutputInterface> xdgOutput() const {
|
||||
return m_xdgOutput;
|
||||
}
|
||||
void createXdgOutput();
|
||||
|
||||
QPointer<KWayland::Server::OutputDeviceInterface> waylandOutputDevice() const {
|
||||
return m_waylandOutputDevice;
|
||||
}
|
||||
|
||||
QPoint globalPos() const {
|
||||
return m_globalPos;
|
||||
}
|
||||
|
||||
QSize rawPhysicalSize() const {
|
||||
return m_physicalSize;
|
||||
}
|
||||
void setRawPhysicalSize(const QSize &set) {
|
||||
m_physicalSize = set;
|
||||
}
|
||||
|
||||
void setOrientation(Qt::ScreenOrientation set) {
|
||||
m_orientation = set;
|
||||
}
|
||||
bool internal() const {
|
||||
return m_internal;
|
||||
}
|
||||
void setInternal(bool set) {
|
||||
m_internal = set;
|
||||
}
|
||||
void setDpmsSupported(bool set) {
|
||||
m_supportsDpms = set;
|
||||
}
|
||||
virtual void updateDpms(KWayland::Server::OutputInterface::DpmsMode mode) {
|
||||
Q_UNUSED(mode);
|
||||
}
|
||||
virtual void updateMode(int modeIndex) {
|
||||
Q_UNUSED(modeIndex);
|
||||
}
|
||||
virtual void transform(KWayland::Server::OutputDeviceInterface::Transform transform) {
|
||||
Q_UNUSED(transform);
|
||||
}
|
||||
|
||||
void setWaylandMode(const QSize &size, int refreshRate);
|
||||
|
||||
QSize orientateSize(const QSize &size) const;
|
||||
|
||||
private:
|
||||
QPointer<KWayland::Server::OutputInterface> m_waylandOutput;
|
||||
QPointer<KWayland::Server::XdgOutputInterface> m_xdgOutput;
|
||||
QPointer<KWayland::Server::OutputDeviceInterface> m_waylandOutputDevice;
|
||||
|
||||
KWayland::Server::OutputInterface::DpmsMode m_dpms = KWayland::Server::OutputInterface::DpmsMode::On;
|
||||
|
||||
QPoint m_globalPos;
|
||||
qreal m_scale = 1;
|
||||
QSize m_physicalSize;
|
||||
Qt::ScreenOrientation m_orientation = Qt::PrimaryOrientation;
|
||||
bool m_internal = false;
|
||||
bool m_supportsDpms = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // KWIN_OUTPUT_H
|
|
@ -259,48 +259,6 @@ target_link_libraries(testScreens
|
|||
add_test(NAME kwin_testScreens COMMAND testScreens)
|
||||
ecm_mark_as_test(testScreens)
|
||||
|
||||
########################################################
|
||||
# Test XrandRScreens
|
||||
########################################################
|
||||
set( testXRandRScreens_SRCS
|
||||
test_xrandr_screens.cpp
|
||||
mock_abstract_client.cpp
|
||||
mock_client.cpp
|
||||
mock_screens.cpp
|
||||
mock_workspace.cpp
|
||||
../screens.cpp
|
||||
../plugins/platforms/x11/standalone/screens_xrandr.cpp
|
||||
../xcbutils.cpp # init of extensions
|
||||
../x11eventfilter.cpp
|
||||
../orientation_sensor.cpp
|
||||
)
|
||||
kconfig_add_kcfg_files(testXRandRScreens_SRCS ../settings.kcfgc)
|
||||
qt5_add_dbus_adaptor( testXRandRScreens_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/../org.kde.kwin.OrientationSensor.xml ${CMAKE_CURRENT_SOURCE_DIR}/../orientation_sensor.h KWin::OrientationSensor)
|
||||
add_executable( testXRandRScreens ${testXRandRScreens_SRCS} )
|
||||
target_link_libraries( testXRandRScreens
|
||||
Qt5::Test
|
||||
Qt5::DBus
|
||||
Qt5::Gui
|
||||
Qt5::Sensors
|
||||
Qt5::Widgets
|
||||
KF5::ConfigCore
|
||||
KF5::ConfigGui
|
||||
KF5::I18n
|
||||
KF5::Notifications
|
||||
KF5::WindowSystem
|
||||
XCB::XCB
|
||||
XCB::RANDR
|
||||
XCB::XFIXES
|
||||
XCB::SYNC
|
||||
XCB::COMPOSITE
|
||||
XCB::DAMAGE
|
||||
XCB::GLX
|
||||
XCB::SHM
|
||||
)
|
||||
|
||||
add_test(NAME kwin-testXRandRScreens COMMAND testXRandRScreens)
|
||||
ecm_mark_as_test(testXRandRScreens)
|
||||
|
||||
########################################################
|
||||
# Test ScreenEdges
|
||||
########################################################
|
||||
|
|
|
@ -1,287 +0,0 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2014 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#include "../plugins/platforms/x11/standalone/screens_xrandr.h"
|
||||
#include "../cursor.h"
|
||||
#include "../xcbutils.h"
|
||||
#include "mock_workspace.h"
|
||||
// Qt
|
||||
#include <QtTest>
|
||||
// system
|
||||
#include <unistd.h>
|
||||
|
||||
Q_LOGGING_CATEGORY(KWIN_CORE, "kwin_core")
|
||||
|
||||
// mocking
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
QPoint Cursor::pos()
|
||||
{
|
||||
return QPoint(0, 0);
|
||||
}
|
||||
} // namespace KWin
|
||||
|
||||
static xcb_window_t s_rootWindow = XCB_WINDOW_NONE;
|
||||
static xcb_connection_t *s_connection = nullptr;
|
||||
|
||||
using namespace KWin;
|
||||
using namespace KWin::Xcb;
|
||||
|
||||
class TestXRandRScreens : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
void testStartup();
|
||||
void testChange();
|
||||
void testMultipleChanges();
|
||||
private:
|
||||
QScopedPointer<QProcess> m_xserver;
|
||||
};
|
||||
|
||||
void TestXRandRScreens::initTestCase()
|
||||
{
|
||||
// TODO: turn into init instead of initTestCase
|
||||
// needs to be initTestCase as KWin::connection caches the first created xcb_connection_t
|
||||
// thus changing X server for each test run would create problems
|
||||
qsrand(QDateTime::currentMSecsSinceEpoch());
|
||||
// first reset just to be sure
|
||||
s_connection = nullptr;
|
||||
s_rootWindow = XCB_WINDOW_NONE;
|
||||
// start X Server
|
||||
m_xserver.reset(new QProcess);
|
||||
// use pipe to pass fd to Xephyr to get back the display id
|
||||
int pipeFds[2];
|
||||
QVERIFY(pipe(pipeFds) == 0);
|
||||
// using Xephyr as Xvfb doesn't support render extension
|
||||
m_xserver->start(QStringLiteral("Xephyr"), QStringList({ QStringLiteral("-displayfd"), QString::number(pipeFds[1]) }));
|
||||
QVERIFY(m_xserver->waitForStarted());
|
||||
QCOMPARE(m_xserver->state(), QProcess::Running);
|
||||
|
||||
// reads from pipe, closes write side
|
||||
close(pipeFds[1]);
|
||||
|
||||
QFile readPipe;
|
||||
QVERIFY(readPipe.open(pipeFds[0], QIODevice::ReadOnly, QFileDevice::AutoCloseHandle));
|
||||
QByteArray displayNumber = readPipe.readLine();
|
||||
readPipe.close();
|
||||
|
||||
displayNumber.prepend(QByteArray(":"));
|
||||
displayNumber.remove(displayNumber.size() -1, 1);
|
||||
|
||||
// create X connection
|
||||
int screen = 0;
|
||||
s_connection = xcb_connect(displayNumber.constData(), &screen);
|
||||
QVERIFY(s_connection);
|
||||
|
||||
// set root window
|
||||
xcb_screen_iterator_t iter = xcb_setup_roots_iterator(xcb_get_setup(s_connection));
|
||||
for (xcb_screen_iterator_t it = xcb_setup_roots_iterator(xcb_get_setup(s_connection));
|
||||
it.rem;
|
||||
--screen, xcb_screen_next(&it)) {
|
||||
if (screen == 0) {
|
||||
s_rootWindow = iter.data->root;
|
||||
break;
|
||||
}
|
||||
}
|
||||
QVERIFY(s_rootWindow != XCB_WINDOW_NONE);
|
||||
qApp->setProperty("x11RootWindow", QVariant::fromValue<quint32>(s_rootWindow));
|
||||
qApp->setProperty("x11Connection", QVariant::fromValue<void*>(s_connection));
|
||||
|
||||
// get the extensions
|
||||
if (!Extensions::self()->isRandrAvailable()) {
|
||||
QSKIP("XRandR extension required");
|
||||
}
|
||||
for (const auto &extension : Extensions::self()->extensions()) {
|
||||
if (extension.name == QByteArrayLiteral("RANDR")) {
|
||||
if (extension.version < 1 * 0x10 + 4) {
|
||||
QSKIP("At least XRandR 1.4 required");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TestXRandRScreens::cleanupTestCase()
|
||||
{
|
||||
Extensions::destroy();
|
||||
// close connection
|
||||
xcb_disconnect(s_connection);
|
||||
s_connection = nullptr;
|
||||
s_rootWindow = XCB_WINDOW_NONE;
|
||||
// kill X
|
||||
m_xserver->terminate();
|
||||
m_xserver->waitForFinished();
|
||||
}
|
||||
|
||||
void TestXRandRScreens::testStartup()
|
||||
{
|
||||
KWin::MockWorkspace ws;
|
||||
QScopedPointer<XRandRScreens> screens(new XRandRScreens(this));
|
||||
QVERIFY(!screens->eventTypes().isEmpty());
|
||||
QCOMPARE(screens->eventTypes().first(), Xcb::Extensions::self()->randrNotifyEvent());
|
||||
QCOMPARE(screens->extension(), 0);
|
||||
QCOMPARE(screens->genericEventTypes(), QVector<int>{0});
|
||||
screens->init();
|
||||
QRect xephyrDefault = QRect(0, 0, 640, 480);
|
||||
QCOMPARE(screens->count(), 1);
|
||||
QCOMPARE(screens->geometry(0), xephyrDefault);
|
||||
QCOMPARE(screens->geometry(1), QRect());
|
||||
QCOMPARE(screens->geometry(-1), QRect());
|
||||
QCOMPARE(static_cast<Screens*>(screens.data())->geometry(), xephyrDefault);
|
||||
QCOMPARE(screens->size(0), xephyrDefault.size());
|
||||
QCOMPARE(screens->size(1), QSize());
|
||||
QCOMPARE(screens->size(-1), QSize());
|
||||
QCOMPARE(static_cast<Screens*>(screens.data())->size(), xephyrDefault.size());
|
||||
// unfortunately we only have one output, so let's try at least to test somewhat
|
||||
QCOMPARE(screens->number(QPoint(0, 0)), 0);
|
||||
QCOMPARE(screens->number(QPoint(639, 479)), 0);
|
||||
QCOMPARE(screens->number(QPoint(1280, 1024)), 0);
|
||||
|
||||
// let's change the mode
|
||||
RandR::CurrentResources resources(s_rootWindow);
|
||||
auto *crtcs = resources.crtcs();
|
||||
auto *modes = xcb_randr_get_screen_resources_current_modes(resources.data());
|
||||
auto *outputs = xcb_randr_get_screen_resources_current_outputs(resources.data());
|
||||
RandR::SetCrtcConfig setter(crtcs[0], resources->timestamp, resources->config_timestamp, 0, 0, modes[0].id, XCB_RANDR_ROTATION_ROTATE_0, 1, outputs);
|
||||
QVERIFY(!setter.isNull());
|
||||
|
||||
// now let's recreate the XRandRScreens
|
||||
screens.reset(new XRandRScreens(this));
|
||||
screens->init();
|
||||
QRect geo = QRect(0, 0, modes[0].width, modes[0].height);
|
||||
QCOMPARE(screens->count(), 1);
|
||||
QCOMPARE(screens->geometry(0), geo);
|
||||
QCOMPARE(static_cast<Screens*>(screens.data())->geometry(), geo);
|
||||
QCOMPARE(screens->size(0), geo.size());
|
||||
QCOMPARE(static_cast<Screens*>(screens.data())->size(), geo.size());
|
||||
}
|
||||
|
||||
void TestXRandRScreens::testChange()
|
||||
{
|
||||
KWin::MockWorkspace ws;
|
||||
QScopedPointer<XRandRScreens> screens(new XRandRScreens(this));
|
||||
screens->init();
|
||||
|
||||
// create some signal spys
|
||||
QSignalSpy changedSpy(screens.data(), SIGNAL(changed()));
|
||||
QVERIFY(changedSpy.isValid());
|
||||
QVERIFY(changedSpy.isEmpty());
|
||||
QVERIFY(changedSpy.wait());
|
||||
changedSpy.clear();
|
||||
QSignalSpy geometrySpy(screens.data(), SIGNAL(geometryChanged()));
|
||||
QVERIFY(geometrySpy.isValid());
|
||||
QVERIFY(geometrySpy.isEmpty());
|
||||
QSignalSpy sizeSpy(screens.data(), SIGNAL(sizeChanged()));
|
||||
QVERIFY(sizeSpy.isValid());
|
||||
QVERIFY(sizeSpy.isEmpty());
|
||||
|
||||
// clear the event loop
|
||||
while (xcb_generic_event_t *e = xcb_poll_for_event(s_connection)) {
|
||||
free(e);
|
||||
}
|
||||
|
||||
// let's change
|
||||
RandR::CurrentResources resources(s_rootWindow);
|
||||
auto *crtcs = resources.crtcs();
|
||||
auto *modes = xcb_randr_get_screen_resources_current_modes(resources.data());
|
||||
auto *outputs = xcb_randr_get_screen_resources_current_outputs(resources.data());
|
||||
RandR::SetCrtcConfig setter(crtcs[0], resources->timestamp, resources->config_timestamp, 0, 0, modes[1].id, XCB_RANDR_ROTATION_ROTATE_0, 1, outputs);
|
||||
xcb_flush(s_connection);
|
||||
QVERIFY(!setter.isNull());
|
||||
QVERIFY(setter->status == XCB_RANDR_SET_CONFIG_SUCCESS);
|
||||
|
||||
xcb_generic_event_t *e = xcb_wait_for_event(s_connection);
|
||||
screens->event(e);
|
||||
free(e);
|
||||
|
||||
QVERIFY(changedSpy.wait());
|
||||
QCOMPARE(changedSpy.size(), 1);
|
||||
QCOMPARE(sizeSpy.size(), 1);
|
||||
QCOMPARE(geometrySpy.size(), 1);
|
||||
QRect geo = QRect(0, 0, modes[1].width, modes[1].height);
|
||||
QCOMPARE(screens->count(), 1);
|
||||
QCOMPARE(screens->geometry(0), geo);
|
||||
QCOMPARE(static_cast<Screens*>(screens.data())->geometry(), geo);
|
||||
QCOMPARE(screens->size(0), geo.size());
|
||||
QCOMPARE(static_cast<Screens*>(screens.data())->size(), geo.size());
|
||||
}
|
||||
|
||||
void TestXRandRScreens::testMultipleChanges()
|
||||
{
|
||||
KWin::MockWorkspace ws;
|
||||
// multiple changes should only hit one changed signal
|
||||
QScopedPointer<XRandRScreens> screens(new XRandRScreens(this));
|
||||
screens->init();
|
||||
|
||||
// create some signal spys
|
||||
QSignalSpy changedSpy(screens.data(), SIGNAL(changed()));
|
||||
QVERIFY(changedSpy.isValid());
|
||||
QVERIFY(changedSpy.isEmpty());
|
||||
QVERIFY(changedSpy.wait());
|
||||
changedSpy.clear();
|
||||
QSignalSpy geometrySpy(screens.data(), SIGNAL(geometryChanged()));
|
||||
QVERIFY(geometrySpy.isValid());
|
||||
QVERIFY(geometrySpy.isEmpty());
|
||||
QSignalSpy sizeSpy(screens.data(), SIGNAL(sizeChanged()));
|
||||
QVERIFY(sizeSpy.isValid());
|
||||
QVERIFY(sizeSpy.isEmpty());
|
||||
|
||||
// clear the event loop
|
||||
while (xcb_generic_event_t *e = xcb_poll_for_event(s_connection)) {
|
||||
free(e);
|
||||
}
|
||||
|
||||
// first change
|
||||
RandR::CurrentResources resources(s_rootWindow);
|
||||
auto *crtcs = resources.crtcs();
|
||||
auto *modes = xcb_randr_get_screen_resources_current_modes(resources.data());
|
||||
auto *outputs = xcb_randr_get_screen_resources_current_outputs(resources.data());
|
||||
RandR::SetCrtcConfig setter(crtcs[0], resources->timestamp, resources->config_timestamp, 0, 0, modes[0].id, XCB_RANDR_ROTATION_ROTATE_0, 1, outputs);
|
||||
QVERIFY(!setter.isNull());
|
||||
QVERIFY(setter->status == XCB_RANDR_SET_CONFIG_SUCCESS);
|
||||
// second change
|
||||
RandR::SetCrtcConfig setter2(crtcs[0], setter->timestamp, resources->config_timestamp, 0, 0, modes[1].id, XCB_RANDR_ROTATION_ROTATE_0, 1, outputs);
|
||||
QVERIFY(!setter2.isNull());
|
||||
QVERIFY(setter2->status == XCB_RANDR_SET_CONFIG_SUCCESS);
|
||||
|
||||
auto passEvent = [&screens]() {
|
||||
xcb_generic_event_t *e = xcb_wait_for_event(s_connection);
|
||||
screens->event(e);
|
||||
free(e);
|
||||
};
|
||||
passEvent();
|
||||
passEvent();
|
||||
|
||||
QVERIFY(changedSpy.wait());
|
||||
QCOMPARE(changedSpy.size(), 1);
|
||||
// previous state was modes[1] so the size didn't change
|
||||
QVERIFY(sizeSpy.isEmpty());
|
||||
QVERIFY(geometrySpy.isEmpty());
|
||||
QRect geo = QRect(0, 0, modes[1].width, modes[1].height);
|
||||
QCOMPARE(screens->count(), 1);
|
||||
QCOMPARE(screens->geometry(0), geo);
|
||||
QCOMPARE(static_cast<Screens*>(screens.data())->geometry(), geo);
|
||||
QCOMPARE(screens->size(0), geo.size());
|
||||
QCOMPARE(static_cast<Screens*>(screens.data())->size(), geo.size());
|
||||
}
|
||||
|
||||
QTEST_GUILESS_MAIN(TestXRandRScreens)
|
||||
#include "test_xrandr_screens.moc"
|
|
@ -51,7 +51,7 @@ namespace KWin
|
|||
{
|
||||
|
||||
DrmOutput::DrmOutput(DrmBackend *backend)
|
||||
: AbstractOutput(backend)
|
||||
: AbstractWaylandOutput(backend)
|
||||
, m_backend(backend)
|
||||
{
|
||||
}
|
||||
|
@ -180,7 +180,7 @@ void DrmOutput::moveCursor(const QPoint &globalPos)
|
|||
{
|
||||
const QMatrix4x4 hotspotMatrix = matrixDisplay(m_backend->softwareCursor().size());
|
||||
|
||||
QPoint p = globalPos-AbstractOutput::globalPos();
|
||||
QPoint p = globalPos - AbstractWaylandOutput::globalPos();
|
||||
switch (orientation()) {
|
||||
case Qt::PrimaryOrientation:
|
||||
case Qt::LandscapeOrientation:
|
||||
|
@ -343,7 +343,7 @@ void DrmOutput::initOutputDevice(drmModeConnector *connector)
|
|||
modes << mode;
|
||||
}
|
||||
|
||||
AbstractOutput::initWaylandOutputDevice(model, manufacturer, m_uuid, modes);
|
||||
AbstractWaylandOutput::initWaylandOutputDevice(model, manufacturer, m_uuid, modes);
|
||||
}
|
||||
|
||||
bool DrmOutput::isCurrentMode(const drmModeModeInfo *mode) const
|
||||
|
@ -819,8 +819,8 @@ QSize DrmOutput::pixelSize() const
|
|||
|
||||
void DrmOutput::setWaylandMode()
|
||||
{
|
||||
AbstractOutput::setWaylandMode(QSize(m_mode.hdisplay, m_mode.vdisplay),
|
||||
refreshRateForMode(&m_mode));
|
||||
AbstractWaylandOutput::setWaylandMode(QSize(m_mode.hdisplay, m_mode.vdisplay),
|
||||
refreshRateForMode(&m_mode));
|
||||
}
|
||||
|
||||
void DrmOutput::pageFlipped()
|
||||
|
|
|
@ -20,7 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#ifndef KWIN_DRM_OUTPUT_H
|
||||
#define KWIN_DRM_OUTPUT_H
|
||||
|
||||
#include "abstract_output.h"
|
||||
#include "abstract_wayland_output.h"
|
||||
#include "drm_pointer.h"
|
||||
#include "drm_object.h"
|
||||
#include "drm_object_plane.h"
|
||||
|
@ -41,7 +41,7 @@ class DrmPlane;
|
|||
class DrmConnector;
|
||||
class DrmCrtc;
|
||||
|
||||
class KWIN_EXPORT DrmOutput : public AbstractOutput
|
||||
class KWIN_EXPORT DrmOutput : public AbstractWaylandOutput
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
|
|
@ -20,7 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
*********************************************************************/
|
||||
#ifndef KWIN_FB_BACKEND_H
|
||||
#define KWIN_FB_BACKEND_H
|
||||
#include "abstract_output.h"
|
||||
#include "abstract_wayland_output.h"
|
||||
#include "platform.h"
|
||||
|
||||
#include <QImage>
|
||||
|
@ -29,12 +29,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
namespace KWin
|
||||
{
|
||||
|
||||
class FramebufferOutput : public AbstractOutput
|
||||
class FramebufferOutput : public AbstractWaylandOutput
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FramebufferOutput(QObject *parent = nullptr) : AbstractOutput(parent) {}
|
||||
FramebufferOutput(QObject *parent = nullptr) : AbstractWaylandOutput(parent) {}
|
||||
virtual ~FramebufferOutput() = default;
|
||||
|
||||
QSize pixelSize() const override {
|
||||
|
@ -45,7 +45,7 @@ public:
|
|||
}
|
||||
|
||||
void setRawPhysicalSize(const QSize &set) {
|
||||
AbstractOutput::setRawPhysicalSize(set);
|
||||
AbstractWaylandOutput::setRawPhysicalSize(set);
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
|
@ -482,7 +482,7 @@ void HwcomposerWindow::present(HWComposerNativeWindowBuffer *buffer)
|
|||
}
|
||||
|
||||
HwcomposerOutput::HwcomposerOutput(hwc_composer_device_1_t *device)
|
||||
: AbstractOutput()
|
||||
: AbstractWaylandOutput()
|
||||
, m_device(device)
|
||||
{
|
||||
uint32_t configs[5];
|
||||
|
|
|
@ -20,7 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#ifndef KWIN_HWCOMPOSER_BACKEND_H
|
||||
#define KWIN_HWCOMPOSER_BACKEND_H
|
||||
#include "platform.h"
|
||||
#include "abstract_output.h"
|
||||
#include "abstract_wayland_output.h"
|
||||
#include "input.h"
|
||||
|
||||
#include <QElapsedTimer>
|
||||
|
@ -47,7 +47,7 @@ namespace KWin
|
|||
class HwcomposerWindow;
|
||||
class BacklightInputEventFilter;
|
||||
|
||||
class HwcomposerOutput : public AbstractOutput
|
||||
class HwcomposerOutput : public AbstractWaylandOutput
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
|
|
@ -23,7 +23,7 @@ namespace KWin
|
|||
{
|
||||
|
||||
VirtualOutput::VirtualOutput(QObject *parent)
|
||||
: AbstractOutput()
|
||||
: AbstractWaylandOutput()
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#ifndef KWIN_VIRTUAL_OUTPUT_H
|
||||
#define KWIN_VIRTUAL_OUTPUT_H
|
||||
|
||||
#include "abstract_output.h"
|
||||
#include "abstract_wayland_output.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QRect>
|
||||
|
@ -29,7 +29,7 @@ namespace KWin
|
|||
{
|
||||
class VirtualBackend;
|
||||
|
||||
class VirtualOutput : public AbstractOutput
|
||||
class VirtualOutput : public AbstractWaylandOutput
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ namespace Wayland
|
|||
using namespace KWayland::Client;
|
||||
|
||||
WaylandOutput::WaylandOutput(Surface *surface, QObject *parent)
|
||||
: AbstractOutput(parent),
|
||||
: AbstractWaylandOutput(parent),
|
||||
m_surface(surface)
|
||||
{
|
||||
connect(surface, &Surface::frameRendered, [this] {
|
||||
|
|
|
@ -20,7 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#ifndef KWIN_WAYLAND_OUTPUT_H
|
||||
#define KWIN_WAYLAND_OUTPUT_H
|
||||
|
||||
#include "abstract_output.h"
|
||||
#include "abstract_wayland_output.h"
|
||||
|
||||
#include <KWayland/Client/xdgshell.h>
|
||||
|
||||
|
@ -46,7 +46,7 @@ namespace Wayland
|
|||
{
|
||||
class WaylandBackend;
|
||||
|
||||
class WaylandOutput : public AbstractOutput
|
||||
class WaylandOutput : public AbstractWaylandOutput
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
|
|
@ -3,6 +3,7 @@ set(X11PLATFORM_SOURCES
|
|||
logging.cpp
|
||||
x11cursor.cpp
|
||||
x11_platform.cpp
|
||||
x11_output.cpp
|
||||
screens_xrandr.cpp
|
||||
windowselector.cpp
|
||||
overlaywindow_x11.cpp
|
||||
|
|
|
@ -18,6 +18,8 @@ You should have received a copy of the GNU General Public License
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#include "screens_xrandr.h"
|
||||
#include "x11_platform.h"
|
||||
|
||||
#ifndef KWIN_UNIT_TEST
|
||||
#include "composite.h"
|
||||
#include "options.h"
|
||||
|
@ -29,160 +31,28 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
namespace KWin
|
||||
{
|
||||
|
||||
XRandRScreens::XRandRScreens(QObject *parent)
|
||||
: Screens(parent)
|
||||
XRandRScreens::XRandRScreens(X11StandalonePlatform *backend, QObject *parent)
|
||||
: OutputScreens(backend, parent)
|
||||
, X11EventFilter(Xcb::Extensions::self()->randrNotifyEvent())
|
||||
, m_backend(backend)
|
||||
{
|
||||
}
|
||||
|
||||
XRandRScreens::~XRandRScreens() = default;
|
||||
|
||||
template <typename T>
|
||||
void XRandRScreens::update()
|
||||
{
|
||||
auto fallback = [this]() {
|
||||
m_geometries << QRect();
|
||||
m_refreshRates << -1.0f;
|
||||
m_names << "Xinerama";
|
||||
setCount(1);
|
||||
};
|
||||
m_geometries.clear();
|
||||
m_names.clear();
|
||||
if (!Xcb::Extensions::self()->isRandrAvailable()) {
|
||||
fallback();
|
||||
return;
|
||||
}
|
||||
T resources(rootWindow());
|
||||
if (resources.isNull()) {
|
||||
fallback();
|
||||
return;
|
||||
}
|
||||
xcb_randr_crtc_t *crtcs = resources.crtcs();
|
||||
xcb_randr_mode_info_t *modes = resources.modes();
|
||||
|
||||
QVector<Xcb::RandR::CrtcInfo> infos(resources->num_crtcs);
|
||||
for (int i = 0; i < resources->num_crtcs; ++i) {
|
||||
infos[i] = Xcb::RandR::CrtcInfo(crtcs[i], resources->config_timestamp);
|
||||
}
|
||||
|
||||
for (int i = 0; i < resources->num_crtcs; ++i) {
|
||||
Xcb::RandR::CrtcInfo info(infos.at(i));
|
||||
|
||||
xcb_randr_output_t *outputs = info.outputs();
|
||||
QVector<Xcb::RandR::OutputInfo> outputInfos(outputs ? resources->num_outputs : 0);
|
||||
if (outputs) {
|
||||
for (int i = 0; i < resources->num_outputs; ++i) {
|
||||
outputInfos[i] = Xcb::RandR::OutputInfo(outputs[i], resources->config_timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
float refreshRate = -1.0f;
|
||||
for (int j = 0; j < resources->num_modes; ++j) {
|
||||
if (info->mode == modes[j].id) {
|
||||
if (modes[j].htotal != 0 && modes[j].vtotal != 0) { // BUG 313996
|
||||
// refresh rate calculation - WTF was wikipedia 1998 when I needed it?
|
||||
int dotclock = modes[j].dot_clock,
|
||||
vtotal = modes[j].vtotal;
|
||||
if (modes[j].mode_flags & XCB_RANDR_MODE_FLAG_INTERLACE)
|
||||
dotclock *= 2;
|
||||
if (modes[j].mode_flags & XCB_RANDR_MODE_FLAG_DOUBLE_SCAN)
|
||||
vtotal *= 2;
|
||||
refreshRate = dotclock/float(modes[j].htotal*vtotal);
|
||||
}
|
||||
break; // found mode
|
||||
}
|
||||
}
|
||||
|
||||
const QRect geo = info.rect();
|
||||
if (geo.isValid()) {
|
||||
m_geometries << geo;
|
||||
m_refreshRates << refreshRate;
|
||||
QString name;
|
||||
for (int j = 0; j < info->num_outputs; ++j) {
|
||||
Xcb::RandR::OutputInfo outputInfo(outputInfos.at(j));
|
||||
if (crtcs[i] == outputInfo->crtc) {
|
||||
name = outputInfo.name();
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_names << name;
|
||||
}
|
||||
}
|
||||
if (m_geometries.isEmpty()) {
|
||||
fallback();
|
||||
return;
|
||||
}
|
||||
|
||||
setCount(m_geometries.count());
|
||||
}
|
||||
|
||||
|
||||
void XRandRScreens::init()
|
||||
{
|
||||
KWin::Screens::init();
|
||||
// we need to call ScreenResources at least once to be able to use current
|
||||
update<Xcb::RandR::ScreenResources>();
|
||||
m_backend->initOutputs();
|
||||
setCount(m_backend->outputs().count());
|
||||
emit changed();
|
||||
}
|
||||
|
||||
QRect XRandRScreens::geometry(int screen) const
|
||||
{
|
||||
if (screen >= m_geometries.size() || screen < 0) {
|
||||
return QRect();
|
||||
}
|
||||
return m_geometries.at(screen).isValid() ? m_geometries.at(screen) :
|
||||
QRect(QPoint(0, 0), displaySize()); // xinerama, lacks RandR
|
||||
}
|
||||
|
||||
QString XRandRScreens::name(int screen) const
|
||||
{
|
||||
if (screen >= m_names.size() || screen < 0) {
|
||||
return QString();
|
||||
}
|
||||
return m_names.at(screen);
|
||||
}
|
||||
|
||||
int XRandRScreens::number(const QPoint &pos) const
|
||||
{
|
||||
int bestScreen = 0;
|
||||
int minDistance = INT_MAX;
|
||||
for (int i = 0; i < m_geometries.size(); ++i) {
|
||||
const QRect &geo = m_geometries.at(i);
|
||||
if (geo.contains(pos)) {
|
||||
return i;
|
||||
}
|
||||
int distance = QPoint(geo.topLeft() - pos).manhattanLength();
|
||||
distance = qMin(distance, QPoint(geo.topRight() - pos).manhattanLength());
|
||||
distance = qMin(distance, QPoint(geo.bottomRight() - pos).manhattanLength());
|
||||
distance = qMin(distance, QPoint(geo.bottomLeft() - pos).manhattanLength());
|
||||
if (distance < minDistance) {
|
||||
minDistance = distance;
|
||||
bestScreen = i;
|
||||
}
|
||||
}
|
||||
return bestScreen;
|
||||
}
|
||||
|
||||
float XRandRScreens::refreshRate(int screen) const
|
||||
{
|
||||
if (screen >= m_refreshRates.size() || screen < 0) {
|
||||
return -1.0f;
|
||||
}
|
||||
return m_refreshRates.at(screen);
|
||||
}
|
||||
|
||||
QSize XRandRScreens::size(int screen) const
|
||||
{
|
||||
const QRect geo = geometry(screen);
|
||||
if (!geo.isValid()) {
|
||||
return QSize();
|
||||
}
|
||||
return geo.size();
|
||||
}
|
||||
|
||||
void XRandRScreens::updateCount()
|
||||
{
|
||||
update<Xcb::RandR::CurrentResources>();
|
||||
m_backend->updateOutputs();
|
||||
setCount(m_backend->outputs().count());
|
||||
}
|
||||
|
||||
bool XRandRScreens::event(xcb_generic_event_t *event)
|
||||
|
|
|
@ -20,40 +20,30 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#ifndef KWIN_SCREENS_XRANDR_H
|
||||
#define KWIN_SCREENS_XRANDR_H
|
||||
// kwin
|
||||
#include "screens.h"
|
||||
#include "outputscreens.h"
|
||||
#include "x11eventfilter.h"
|
||||
// Qt
|
||||
#include <QVector>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
class X11StandalonePlatform;
|
||||
|
||||
class XRandRScreens : public Screens, public X11EventFilter
|
||||
class XRandRScreens : public OutputScreens, public X11EventFilter
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
XRandRScreens(QObject *parent);
|
||||
XRandRScreens(X11StandalonePlatform *backend, QObject *parent = nullptr);
|
||||
virtual ~XRandRScreens();
|
||||
void init() override;
|
||||
QRect geometry(int screen) const override;
|
||||
QString name(int screen) const override;
|
||||
int number(const QPoint& pos) const override;
|
||||
float refreshRate(int screen) const override;
|
||||
QSize size(int screen) const override;
|
||||
|
||||
QSize displaySize() const override;
|
||||
|
||||
using QObject::event;
|
||||
bool event(xcb_generic_event_t *event) override;
|
||||
|
||||
protected Q_SLOTS:
|
||||
private:
|
||||
void updateCount() override;
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
void update();
|
||||
QVector<QRect> m_geometries;
|
||||
QVector<float> m_refreshRates;
|
||||
QVector<QString> m_names;
|
||||
X11StandalonePlatform *m_backend;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
|
64
plugins/platforms/x11/standalone/x11_output.cpp
Normal file
64
plugins/platforms/x11/standalone/x11_output.cpp
Normal file
|
@ -0,0 +1,64 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright 2019 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#include "x11_output.h"
|
||||
#include "screens.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
X11Output::X11Output(QObject *parent)
|
||||
: AbstractOutput(parent)
|
||||
{
|
||||
}
|
||||
|
||||
QString X11Output::name() const
|
||||
{
|
||||
return m_name;
|
||||
}
|
||||
|
||||
void X11Output::setName(QString set)
|
||||
{
|
||||
m_name = set;
|
||||
}
|
||||
|
||||
QRect X11Output::geometry() const
|
||||
{
|
||||
if (m_geometry.isValid()) {
|
||||
return m_geometry;
|
||||
}
|
||||
return QRect(QPoint(0, 0), Screens::self()->displaySize()); // xinerama, lacks RandR
|
||||
}
|
||||
|
||||
void X11Output::setGeometry(QRect set)
|
||||
{
|
||||
m_geometry = set;
|
||||
}
|
||||
|
||||
int X11Output::refreshRate() const
|
||||
{
|
||||
return m_refreshRate;
|
||||
}
|
||||
|
||||
void X11Output::setRefreshRate(int set)
|
||||
{
|
||||
m_refreshRate = set;
|
||||
}
|
||||
|
||||
}
|
64
plugins/platforms/x11/standalone/x11_output.h
Normal file
64
plugins/platforms/x11/standalone/x11_output.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright 2019 Roman Gilg <subdiff@gmail.com>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#ifndef KWIN_X11_OUTPUT_H
|
||||
#define KWIN_X11_OUTPUT_H
|
||||
|
||||
#include "abstract_output.h"
|
||||
#include <kwin_export.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QRect>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
/**
|
||||
* X11 output representation
|
||||
**/
|
||||
class KWIN_EXPORT X11Output : public AbstractOutput
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit X11Output(QObject *parent = nullptr);
|
||||
virtual ~X11Output() = default;
|
||||
|
||||
QString name() const override;
|
||||
void setName(QString set);
|
||||
/**
|
||||
* The geometry of this output in global compositor co-ordinates (i.e scaled)
|
||||
**/
|
||||
QRect geometry() const override;
|
||||
void setGeometry(QRect set);
|
||||
|
||||
/**
|
||||
* Current refresh rate in 1/ms.
|
||||
**/
|
||||
int refreshRate() const override;
|
||||
void setRefreshRate(int set);
|
||||
|
||||
private:
|
||||
QString m_name;
|
||||
QRect m_geometry;
|
||||
int m_refreshRate;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -42,6 +42,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "non_composited_outline.h"
|
||||
#include "workspace.h"
|
||||
#include "x11_decoration_renderer.h"
|
||||
#include "x11_output.h"
|
||||
#include "xcbutils.h"
|
||||
|
||||
#include <kwinxrenderutils.h>
|
||||
|
||||
|
@ -106,7 +108,7 @@ void X11StandalonePlatform::init()
|
|||
|
||||
Screens *X11StandalonePlatform::createScreens(QObject *parent)
|
||||
{
|
||||
return new XRandRScreens(parent);
|
||||
return new XRandRScreens(this, parent);
|
||||
}
|
||||
|
||||
OpenGLBackend *X11StandalonePlatform::createOpenGLBackend()
|
||||
|
@ -439,4 +441,109 @@ QVector<CompositingType> X11StandalonePlatform::supportedCompositors() const
|
|||
return compositors;
|
||||
}
|
||||
|
||||
void X11StandalonePlatform::initOutputs()
|
||||
{
|
||||
doUpdateOutputs<Xcb::RandR::ScreenResources>();
|
||||
}
|
||||
|
||||
void X11StandalonePlatform::updateOutputs()
|
||||
{
|
||||
doUpdateOutputs<Xcb::RandR::CurrentResources>();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void X11StandalonePlatform::doUpdateOutputs()
|
||||
{
|
||||
auto fallback = [this]() {
|
||||
auto *o = new X11Output(this);
|
||||
o->setRefreshRate(-1.0f);
|
||||
o->setName(QStringLiteral("Xinerama"));
|
||||
m_outputs << o;
|
||||
};
|
||||
|
||||
// TODO: instead of resetting all outputs, check if new output is added/removed
|
||||
// or still available and leave still available outputs in m_outputs
|
||||
// untouched (like in DRM backend)
|
||||
qDeleteAll(m_outputs);
|
||||
m_outputs.clear();
|
||||
|
||||
if (!Xcb::Extensions::self()->isRandrAvailable()) {
|
||||
fallback();
|
||||
return;
|
||||
}
|
||||
T resources(rootWindow());
|
||||
if (resources.isNull()) {
|
||||
fallback();
|
||||
return;
|
||||
}
|
||||
xcb_randr_crtc_t *crtcs = resources.crtcs();
|
||||
xcb_randr_mode_info_t *modes = resources.modes();
|
||||
|
||||
QVector<Xcb::RandR::CrtcInfo> infos(resources->num_crtcs);
|
||||
for (int i = 0; i < resources->num_crtcs; ++i) {
|
||||
infos[i] = Xcb::RandR::CrtcInfo(crtcs[i], resources->config_timestamp);
|
||||
}
|
||||
|
||||
for (int i = 0; i < resources->num_crtcs; ++i) {
|
||||
Xcb::RandR::CrtcInfo info(infos.at(i));
|
||||
|
||||
xcb_randr_output_t *outputs = info.outputs();
|
||||
QVector<Xcb::RandR::OutputInfo> outputInfos(outputs ? resources->num_outputs : 0);
|
||||
if (outputs) {
|
||||
for (int i = 0; i < resources->num_outputs; ++i) {
|
||||
outputInfos[i] = Xcb::RandR::OutputInfo(outputs[i], resources->config_timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
float refreshRate = -1.0f;
|
||||
for (int j = 0; j < resources->num_modes; ++j) {
|
||||
if (info->mode == modes[j].id) {
|
||||
if (modes[j].htotal != 0 && modes[j].vtotal != 0) { // BUG 313996
|
||||
// refresh rate calculation - WTF was wikipedia 1998 when I needed it?
|
||||
int dotclock = modes[j].dot_clock,
|
||||
vtotal = modes[j].vtotal;
|
||||
if (modes[j].mode_flags & XCB_RANDR_MODE_FLAG_INTERLACE)
|
||||
dotclock *= 2;
|
||||
if (modes[j].mode_flags & XCB_RANDR_MODE_FLAG_DOUBLE_SCAN)
|
||||
vtotal *= 2;
|
||||
refreshRate = dotclock/float(modes[j].htotal*vtotal);
|
||||
}
|
||||
break; // found mode
|
||||
}
|
||||
}
|
||||
|
||||
const QRect geo = info.rect();
|
||||
if (geo.isValid()) {
|
||||
auto *o = new X11Output(this);
|
||||
o->setGeometry(geo);
|
||||
o->setRefreshRate(refreshRate);
|
||||
|
||||
QString name;
|
||||
for (int j = 0; j < info->num_outputs; ++j) {
|
||||
Xcb::RandR::OutputInfo outputInfo(outputInfos.at(j));
|
||||
if (crtcs[i] == outputInfo->crtc) {
|
||||
name = outputInfo.name();
|
||||
break;
|
||||
}
|
||||
}
|
||||
o->setName(name);
|
||||
m_outputs << o;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_outputs.isEmpty()) {
|
||||
fallback();
|
||||
}
|
||||
}
|
||||
|
||||
Outputs X11StandalonePlatform::outputs() const
|
||||
{
|
||||
return m_outputs;
|
||||
}
|
||||
|
||||
Outputs X11StandalonePlatform::enabledOutputs() const
|
||||
{
|
||||
return m_outputs;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ class SyncFilter;
|
|||
class XInputIntegration;
|
||||
class WindowSelector;
|
||||
class X11EventFilter;
|
||||
class X11Output;
|
||||
|
||||
class KWIN_EXPORT X11StandalonePlatform : public Platform
|
||||
{
|
||||
|
@ -71,6 +72,12 @@ public:
|
|||
void createEffectsHandler(Compositor *compositor, Scene *scene) override;
|
||||
QVector<CompositingType> supportedCompositors() const override;
|
||||
|
||||
void initOutputs();
|
||||
void updateOutputs();
|
||||
|
||||
Outputs outputs() const override;
|
||||
Outputs enabledOutputs() const override;
|
||||
|
||||
protected:
|
||||
void doHideCursor() override;
|
||||
void doShowCursor() override;
|
||||
|
@ -87,6 +94,9 @@ private:
|
|||
**/
|
||||
static bool hasGlx();
|
||||
|
||||
template <typename T>
|
||||
void doUpdateOutputs();
|
||||
|
||||
XInputIntegration *m_xinputIntegration = nullptr;
|
||||
QThread *m_openGLFreezeProtectionThread = nullptr;
|
||||
QTimer *m_openGLFreezeProtection = nullptr;
|
||||
|
@ -95,6 +105,7 @@ private:
|
|||
QScopedPointer<X11EventFilter> m_screenEdgesFilter;
|
||||
std::unique_ptr<SyncFilter> m_syncFilter;
|
||||
|
||||
QVector<X11Output*> m_outputs;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ namespace KWin
|
|||
{
|
||||
|
||||
X11WindowedOutput::X11WindowedOutput(X11WindowedBackend *backend)
|
||||
: AbstractOutput(backend)
|
||||
: AbstractWaylandOutput(backend)
|
||||
, m_backend(backend)
|
||||
{
|
||||
m_window = xcb_generate_id(m_backend->connection());
|
||||
|
|
|
@ -20,7 +20,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#ifndef KWIN_X11WINDOWED_OUTPUT_H
|
||||
#define KWIN_X11WINDOWED_OUTPUT_H
|
||||
|
||||
#include "abstract_output.h"
|
||||
#include "abstract_wayland_output.h"
|
||||
#include <kwin_export.h>
|
||||
|
||||
#include <QObject>
|
||||
|
@ -38,7 +38,7 @@ class X11WindowedBackend;
|
|||
/**
|
||||
* Wayland outputs in a nested X11 setup
|
||||
**/
|
||||
class KWIN_EXPORT X11WindowedOutput : public AbstractOutput
|
||||
class KWIN_EXPORT X11WindowedOutput : public AbstractWaylandOutput
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
|
Loading…
Reference in a new issue