[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
icc-effect-5.17.5
Roman Gilg 2019-06-13 11:36:07 +02:00
parent 8040c559cb
commit 1a11abc821
25 changed files with 746 additions and 861 deletions

View File

@ -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

View File

@ -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;
}
}

View File

@ -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
View 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
View 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

View File

@ -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
########################################################

View File

@ -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"

View File

@ -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()

View File

@ -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:

View File

@ -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:

View File

@ -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];

View File

@ -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:

View File

@ -23,7 +23,7 @@ namespace KWin
{
VirtualOutput::VirtualOutput(QObject *parent)
: AbstractOutput()
: AbstractWaylandOutput()
{
Q_UNUSED(parent);

View File

@ -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

View File

@ -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] {

View File

@ -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:

View File

@ -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

View File

@ -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)

View File

@ -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

View 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;
}
}

View 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

View File

@ -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;
}
}

View File

@ -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;
};
}

View File

@ -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());

View File

@ -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: