From 1a11abc821f474d9ab9814cfc159927e83c3a4d6 Mon Sep 17 00:00:00 2001 From: Roman Gilg Date: Thu, 13 Jun 2019 11:36:07 +0200 Subject: [PATCH] [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 --- CMakeLists.txt | 1 + abstract_output.cpp | 223 -------------- abstract_output.h | 146 ++------- abstract_wayland_output.cpp | 259 ++++++++++++++++ abstract_wayland_output.h | 182 +++++++++++ autotests/CMakeLists.txt | 42 --- autotests/test_xrandr_screens.cpp | 287 ------------------ plugins/platforms/drm/drm_output.cpp | 10 +- plugins/platforms/drm/drm_output.h | 4 +- plugins/platforms/fbdev/fb_backend.h | 8 +- .../hwcomposer/hwcomposer_backend.cpp | 2 +- .../platforms/hwcomposer/hwcomposer_backend.h | 4 +- plugins/platforms/virtual/virtual_output.cpp | 2 +- plugins/platforms/virtual/virtual_output.h | 4 +- plugins/platforms/wayland/wayland_output.cpp | 2 +- plugins/platforms/wayland/wayland_output.h | 4 +- .../platforms/x11/standalone/CMakeLists.txt | 1 + .../x11/standalone/screens_xrandr.cpp | 148 +-------- .../platforms/x11/standalone/screens_xrandr.h | 24 +- .../platforms/x11/standalone/x11_output.cpp | 64 ++++ plugins/platforms/x11/standalone/x11_output.h | 64 ++++ .../platforms/x11/standalone/x11_platform.cpp | 109 ++++++- .../platforms/x11/standalone/x11_platform.h | 11 + .../x11/windowed/x11windowed_output.cpp | 2 +- .../x11/windowed/x11windowed_output.h | 4 +- 25 files changed, 746 insertions(+), 861 deletions(-) create mode 100644 abstract_wayland_output.cpp create mode 100644 abstract_wayland_output.h delete mode 100644 autotests/test_xrandr_screens.cpp create mode 100644 plugins/platforms/x11/standalone/x11_output.cpp create mode 100644 plugins/platforms/x11/standalone/x11_output.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2de1a9a6af..d31a9abf67 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/abstract_output.cpp b/abstract_output.cpp index eaba345c76..1536697c90 100644 --- a/abstract_output.cpp +++ b/abstract_output.cpp @@ -18,12 +18,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #include "abstract_output.h" -#include "wayland_server.h" -// KWayland -#include -#include -#include // KF5 #include @@ -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 &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; } } diff --git a/abstract_output.h b/abstract_output.h index f6bd238d73..63ae4ba1dd 100644 --- a/abstract_output.h +++ b/abstract_output.h @@ -2,7 +2,7 @@ KWin - the KDE window manager This file is part of the KDE project. -Copyright 2018 Roman Gilg +Copyright 2019 Roman Gilg 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 . *********************************************************************/ -#ifndef KWIN_OUTPUT_H -#define KWIN_OUTPUT_H +#ifndef KWIN_ABSTRACT_OUTPUT_H +#define KWIN_ABSTRACT_OUTPUT_H -#include #include #include -#include -#include #include #include -#include - -#include -#include - -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 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 &modes); - - QPointer xdgOutput() const { - return m_xdgOutput; - } - void createXdgOutput(); - - QPointer 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 m_waylandOutput; - QPointer m_xdgOutput; - QPointer 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 diff --git a/abstract_wayland_output.cpp b/abstract_wayland_output.cpp new file mode 100644 index 0000000000..c08a073046 --- /dev/null +++ b/abstract_wayland_output.cpp @@ -0,0 +1,259 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright 2019 Roman Gilg + +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 . +*********************************************************************/ +#include "abstract_wayland_output.h" +#include "wayland_server.h" + +// KWayland +#include +#include +#include +// KF5 +#include + +#include + +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 &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; +} + +} diff --git a/abstract_wayland_output.h b/abstract_wayland_output.h new file mode 100644 index 0000000000..c11c3e7dc9 --- /dev/null +++ b/abstract_wayland_output.h @@ -0,0 +1,182 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright 2019 Roman Gilg + +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 . +*********************************************************************/ +#ifndef KWIN_ABSTRACT_WAYLAND_OUTPUT_H +#define KWIN_ABSTRACT_WAYLAND_OUTPUT_H + +#include "abstract_output.h" +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +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 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 &modes); + + QPointer xdgOutput() const { + return m_xdgOutput; + } + void createXdgOutput(); + + QPointer 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 m_waylandOutput; + QPointer m_xdgOutput; + QPointer 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 diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt index 0aed9e5edc..d80a6f4cc7 100644 --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -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 ######################################################## diff --git a/autotests/test_xrandr_screens.cpp b/autotests/test_xrandr_screens.cpp deleted file mode 100644 index e0f6b14a53..0000000000 --- a/autotests/test_xrandr_screens.cpp +++ /dev/null @@ -1,287 +0,0 @@ -/******************************************************************** -KWin - the KDE window manager -This file is part of the KDE project. - -Copyright (C) 2014 Martin Gräßlin - -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 . -*********************************************************************/ -#include "../plugins/platforms/x11/standalone/screens_xrandr.h" -#include "../cursor.h" -#include "../xcbutils.h" -#include "mock_workspace.h" -// Qt -#include -// system -#include - -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 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(s_rootWindow)); - qApp->setProperty("x11Connection", QVariant::fromValue(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 screens(new XRandRScreens(this)); - QVERIFY(!screens->eventTypes().isEmpty()); - QCOMPARE(screens->eventTypes().first(), Xcb::Extensions::self()->randrNotifyEvent()); - QCOMPARE(screens->extension(), 0); - QCOMPARE(screens->genericEventTypes(), QVector{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.data())->geometry(), xephyrDefault); - QCOMPARE(screens->size(0), xephyrDefault.size()); - QCOMPARE(screens->size(1), QSize()); - QCOMPARE(screens->size(-1), QSize()); - QCOMPARE(static_cast(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.data())->geometry(), geo); - QCOMPARE(screens->size(0), geo.size()); - QCOMPARE(static_cast(screens.data())->size(), geo.size()); -} - -void TestXRandRScreens::testChange() -{ - KWin::MockWorkspace ws; - QScopedPointer 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.data())->geometry(), geo); - QCOMPARE(screens->size(0), geo.size()); - QCOMPARE(static_cast(screens.data())->size(), geo.size()); -} - -void TestXRandRScreens::testMultipleChanges() -{ - KWin::MockWorkspace ws; - // multiple changes should only hit one changed signal - QScopedPointer 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.data())->geometry(), geo); - QCOMPARE(screens->size(0), geo.size()); - QCOMPARE(static_cast(screens.data())->size(), geo.size()); -} - -QTEST_GUILESS_MAIN(TestXRandRScreens) -#include "test_xrandr_screens.moc" diff --git a/plugins/platforms/drm/drm_output.cpp b/plugins/platforms/drm/drm_output.cpp index 3710295624..1bce5d3a57 100644 --- a/plugins/platforms/drm/drm_output.cpp +++ b/plugins/platforms/drm/drm_output.cpp @@ -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() diff --git a/plugins/platforms/drm/drm_output.h b/plugins/platforms/drm/drm_output.h index bc77d89919..c2199e677f 100644 --- a/plugins/platforms/drm/drm_output.h +++ b/plugins/platforms/drm/drm_output.h @@ -20,7 +20,7 @@ along with this program. If not, see . #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: diff --git a/plugins/platforms/fbdev/fb_backend.h b/plugins/platforms/fbdev/fb_backend.h index 8b7c8ab43c..666cf3f167 100644 --- a/plugins/platforms/fbdev/fb_backend.h +++ b/plugins/platforms/fbdev/fb_backend.h @@ -20,7 +20,7 @@ along with this program. If not, see . *********************************************************************/ #ifndef KWIN_FB_BACKEND_H #define KWIN_FB_BACKEND_H -#include "abstract_output.h" +#include "abstract_wayland_output.h" #include "platform.h" #include @@ -29,12 +29,12 @@ along with this program. If not, see . 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: diff --git a/plugins/platforms/hwcomposer/hwcomposer_backend.cpp b/plugins/platforms/hwcomposer/hwcomposer_backend.cpp index da1ca11bdd..58a9b88c02 100644 --- a/plugins/platforms/hwcomposer/hwcomposer_backend.cpp +++ b/plugins/platforms/hwcomposer/hwcomposer_backend.cpp @@ -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]; diff --git a/plugins/platforms/hwcomposer/hwcomposer_backend.h b/plugins/platforms/hwcomposer/hwcomposer_backend.h index 35fbfb7af2..cadaff2684 100644 --- a/plugins/platforms/hwcomposer/hwcomposer_backend.h +++ b/plugins/platforms/hwcomposer/hwcomposer_backend.h @@ -20,7 +20,7 @@ along with this program. If not, see . #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 @@ -47,7 +47,7 @@ namespace KWin class HwcomposerWindow; class BacklightInputEventFilter; -class HwcomposerOutput : public AbstractOutput +class HwcomposerOutput : public AbstractWaylandOutput { Q_OBJECT public: diff --git a/plugins/platforms/virtual/virtual_output.cpp b/plugins/platforms/virtual/virtual_output.cpp index 05ba336604..28ede1c276 100644 --- a/plugins/platforms/virtual/virtual_output.cpp +++ b/plugins/platforms/virtual/virtual_output.cpp @@ -23,7 +23,7 @@ namespace KWin { VirtualOutput::VirtualOutput(QObject *parent) - : AbstractOutput() + : AbstractWaylandOutput() { Q_UNUSED(parent); diff --git a/plugins/platforms/virtual/virtual_output.h b/plugins/platforms/virtual/virtual_output.h index 450a868699..4569c7dd07 100644 --- a/plugins/platforms/virtual/virtual_output.h +++ b/plugins/platforms/virtual/virtual_output.h @@ -20,7 +20,7 @@ along with this program. If not, see . #ifndef KWIN_VIRTUAL_OUTPUT_H #define KWIN_VIRTUAL_OUTPUT_H -#include "abstract_output.h" +#include "abstract_wayland_output.h" #include #include @@ -29,7 +29,7 @@ namespace KWin { class VirtualBackend; -class VirtualOutput : public AbstractOutput +class VirtualOutput : public AbstractWaylandOutput { Q_OBJECT diff --git a/plugins/platforms/wayland/wayland_output.cpp b/plugins/platforms/wayland/wayland_output.cpp index 828f5d622b..07aca39123 100644 --- a/plugins/platforms/wayland/wayland_output.cpp +++ b/plugins/platforms/wayland/wayland_output.cpp @@ -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] { diff --git a/plugins/platforms/wayland/wayland_output.h b/plugins/platforms/wayland/wayland_output.h index 5dfbf3f38e..09e749ecb4 100644 --- a/plugins/platforms/wayland/wayland_output.h +++ b/plugins/platforms/wayland/wayland_output.h @@ -20,7 +20,7 @@ along with this program. If not, see . #ifndef KWIN_WAYLAND_OUTPUT_H #define KWIN_WAYLAND_OUTPUT_H -#include "abstract_output.h" +#include "abstract_wayland_output.h" #include @@ -46,7 +46,7 @@ namespace Wayland { class WaylandBackend; -class WaylandOutput : public AbstractOutput +class WaylandOutput : public AbstractWaylandOutput { Q_OBJECT public: diff --git a/plugins/platforms/x11/standalone/CMakeLists.txt b/plugins/platforms/x11/standalone/CMakeLists.txt index 266ce176b3..80c139b772 100644 --- a/plugins/platforms/x11/standalone/CMakeLists.txt +++ b/plugins/platforms/x11/standalone/CMakeLists.txt @@ -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 diff --git a/plugins/platforms/x11/standalone/screens_xrandr.cpp b/plugins/platforms/x11/standalone/screens_xrandr.cpp index 4f552834ca..aec04c92fb 100644 --- a/plugins/platforms/x11/standalone/screens_xrandr.cpp +++ b/plugins/platforms/x11/standalone/screens_xrandr.cpp @@ -18,6 +18,8 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ #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 . 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 -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 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 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(); + 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(); + m_backend->updateOutputs(); + setCount(m_backend->outputs().count()); } bool XRandRScreens::event(xcb_generic_event_t *event) diff --git a/plugins/platforms/x11/standalone/screens_xrandr.h b/plugins/platforms/x11/standalone/screens_xrandr.h index 5a7a748794..e0f9b6a084 100644 --- a/plugins/platforms/x11/standalone/screens_xrandr.h +++ b/plugins/platforms/x11/standalone/screens_xrandr.h @@ -20,40 +20,30 @@ along with this program. If not, see . #ifndef KWIN_SCREENS_XRANDR_H #define KWIN_SCREENS_XRANDR_H // kwin -#include "screens.h" +#include "outputscreens.h" #include "x11eventfilter.h" -// Qt -#include 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 - void update(); - QVector m_geometries; - QVector m_refreshRates; - QVector m_names; + X11StandalonePlatform *m_backend; }; } // namespace diff --git a/plugins/platforms/x11/standalone/x11_output.cpp b/plugins/platforms/x11/standalone/x11_output.cpp new file mode 100644 index 0000000000..9a1e30a08c --- /dev/null +++ b/plugins/platforms/x11/standalone/x11_output.cpp @@ -0,0 +1,64 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright 2019 Roman Gilg + +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 . +*********************************************************************/ +#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; +} + +} diff --git a/plugins/platforms/x11/standalone/x11_output.h b/plugins/platforms/x11/standalone/x11_output.h new file mode 100644 index 0000000000..d5f094058f --- /dev/null +++ b/plugins/platforms/x11/standalone/x11_output.h @@ -0,0 +1,64 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright 2019 Roman Gilg + +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 . +*********************************************************************/ +#ifndef KWIN_X11_OUTPUT_H +#define KWIN_X11_OUTPUT_H + +#include "abstract_output.h" +#include + +#include +#include + +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 diff --git a/plugins/platforms/x11/standalone/x11_platform.cpp b/plugins/platforms/x11/standalone/x11_platform.cpp index e7b5fba2f0..215af1ca24 100644 --- a/plugins/platforms/x11/standalone/x11_platform.cpp +++ b/plugins/platforms/x11/standalone/x11_platform.cpp @@ -42,6 +42,8 @@ along with this program. If not, see . #include "non_composited_outline.h" #include "workspace.h" #include "x11_decoration_renderer.h" +#include "x11_output.h" +#include "xcbutils.h" #include @@ -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 X11StandalonePlatform::supportedCompositors() const return compositors; } +void X11StandalonePlatform::initOutputs() +{ + doUpdateOutputs(); +} + +void X11StandalonePlatform::updateOutputs() +{ + doUpdateOutputs(); +} + +template +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 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 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; +} + } diff --git a/plugins/platforms/x11/standalone/x11_platform.h b/plugins/platforms/x11/standalone/x11_platform.h index 7321775378..44e4e4ad01 100644 --- a/plugins/platforms/x11/standalone/x11_platform.h +++ b/plugins/platforms/x11/standalone/x11_platform.h @@ -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 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 + void doUpdateOutputs(); + XInputIntegration *m_xinputIntegration = nullptr; QThread *m_openGLFreezeProtectionThread = nullptr; QTimer *m_openGLFreezeProtection = nullptr; @@ -95,6 +105,7 @@ private: QScopedPointer m_screenEdgesFilter; std::unique_ptr m_syncFilter; + QVector m_outputs; }; } diff --git a/plugins/platforms/x11/windowed/x11windowed_output.cpp b/plugins/platforms/x11/windowed/x11windowed_output.cpp index cbcbd65a5f..9889d32760 100644 --- a/plugins/platforms/x11/windowed/x11windowed_output.cpp +++ b/plugins/platforms/x11/windowed/x11windowed_output.cpp @@ -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()); diff --git a/plugins/platforms/x11/windowed/x11windowed_output.h b/plugins/platforms/x11/windowed/x11windowed_output.h index 06390c2b55..893b1882d3 100644 --- a/plugins/platforms/x11/windowed/x11windowed_output.h +++ b/plugins/platforms/x11/windowed/x11windowed_output.h @@ -20,7 +20,7 @@ along with this program. If not, see . #ifndef KWIN_X11WINDOWED_OUTPUT_H #define KWIN_X11WINDOWED_OUTPUT_H -#include "abstract_output.h" +#include "abstract_wayland_output.h" #include #include @@ -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: