diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -470,6 +470,7 @@ 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.h b/abstract_output.h --- 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 @@ 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,129 +43,36 @@ 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; } virtual bool setGammaRamp(const ColorCorrect::GammaRamp &gamma) { 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_output.cpp b/abstract_output.cpp --- a/abstract_output.cpp +++ b/abstract_output.cpp @@ -18,12 +18,7 @@ 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() { - 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_wayland_output.h copy from abstract_output.h copy to abstract_wayland_output.h --- a/abstract_output.h +++ b/abstract_wayland_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,9 +17,10 @@ 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_WAYLAND_OUTPUT_H +#define KWIN_ABSTRACT_WAYLAND_OUTPUT_H +#include "abstract_output.h" #include #include @@ -48,44 +49,40 @@ namespace KWin { -namespace ColorCorrect { -struct GammaRamp; -} - /** * Generic output representation in a Wayland session **/ -class KWIN_EXPORT AbstractOutput : public QObject +class KWIN_EXPORT AbstractWaylandOutput : public AbstractOutput { Q_OBJECT public: - explicit AbstractOutput(QObject *parent = nullptr); - virtual ~AbstractOutput(); + explicit AbstractWaylandOutput(QObject *parent = nullptr); + virtual ~AbstractWaylandOutput(); - QString name() const; + QString name() const override; bool isEnabled() const { return !m_waylandOutput.isNull(); } virtual QSize pixelSize() const = 0; - qreal scale() const { + qreal scale() const override { 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 { + 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; + int refreshRate() const override; - bool isInternal() const { + bool isInternal() const override { return m_internal; } @@ -109,14 +106,6 @@ **/ void setEnabled(bool enable); - virtual int getGammaRampSize() const { - return 0; - } - virtual bool setGammaRamp(const ColorCorrect::GammaRamp &gamma) { - Q_UNUSED(gamma); - return false; - } - Q_SIGNALS: void modeChanged(); @@ -150,6 +139,9 @@ void setOrientation(Qt::ScreenOrientation set) { m_orientation = set; } + bool internal() const { + return m_internal; + } void setInternal(bool set) { m_internal = set; } diff --git a/abstract_output.cpp b/abstract_wayland_output.cpp copy from abstract_output.cpp copy to abstract_wayland_output.cpp --- a/abstract_output.cpp +++ b/abstract_wayland_output.cpp @@ -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,7 +17,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 "abstract_wayland_output.h" #include "wayland_server.h" // KWayland @@ -32,45 +32,45 @@ namespace KWin { -AbstractOutput::AbstractOutput(QObject *parent) - : QObject(parent) +AbstractWaylandOutput::AbstractWaylandOutput(QObject *parent) + : AbstractOutput(parent) { } -AbstractOutput::~AbstractOutput() +AbstractWaylandOutput::~AbstractWaylandOutput() { delete m_waylandOutputDevice.data(); delete m_xdgOutput.data(); delete m_waylandOutput.data(); } -QString AbstractOutput::name() const +QString AbstractWaylandOutput::name() const { if (!m_waylandOutput) { return i18n("unknown"); } return QStringLiteral("%1 %2").arg(m_waylandOutput->manufacturer()).arg(m_waylandOutput->model()); } -QRect AbstractOutput::geometry() const +QRect AbstractWaylandOutput::geometry() const { return QRect(m_globalPos, pixelSize() / scale()); } -QSize AbstractOutput::physicalSize() const +QSize AbstractWaylandOutput::physicalSize() const { return orientateSize(m_physicalSize); } -int AbstractOutput::refreshRate() const +int AbstractWaylandOutput::refreshRate() const { if (!m_waylandOutput) { return 60000; } return m_waylandOutput->refreshRate(); } -void AbstractOutput::setGlobalPos(const QPoint &pos) +void AbstractWaylandOutput::setGlobalPos(const QPoint &pos) { m_globalPos = pos; if (m_waylandOutput) { @@ -85,7 +85,7 @@ } } -void AbstractOutput::setScale(qreal scale) +void AbstractWaylandOutput::setScale(qreal scale) { m_scale = scale; if (m_waylandOutput) { @@ -107,9 +107,9 @@ emit modeChanged(); } -void AbstractOutput::setChanges(KWayland::Server::OutputChangeSet *changes) +void AbstractWaylandOutput::setChanges(KWayland::Server::OutputChangeSet *changes) { - qCDebug(KWIN_CORE) << "Set changes in AbstractOutput."; + qCDebug(KWIN_CORE) << "Set changes in AbstractWaylandOutput."; Q_ASSERT(!m_waylandOutputDevice.isNull()); if (!changes) { @@ -137,7 +137,7 @@ } } -void AbstractOutput::setEnabled(bool enable) +void AbstractWaylandOutput::setEnabled(bool enable) { if (enable == isEnabled()) { return; @@ -153,7 +153,7 @@ KWayland::Server::OutputDeviceInterface::Enablement::Disabled); } -void AbstractOutput::setWaylandMode(const QSize &size, int refreshRate) +void AbstractWaylandOutput::setWaylandMode(const QSize &size, int refreshRate) { if (m_waylandOutput.isNull()) { return; @@ -165,18 +165,15 @@ } } -void AbstractOutput::createXdgOutput() +void AbstractWaylandOutput::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() +void AbstractWaylandOutput::initWaylandOutput() { Q_ASSERT(m_waylandOutputDevice); @@ -222,7 +219,7 @@ ); } -void AbstractOutput::initWaylandOutputDevice(const QString &model, +void AbstractWaylandOutput::initWaylandOutputDevice(const QString &model, const QString &manufacturer, const QByteArray &uuid, const QVector &modes) @@ -251,7 +248,7 @@ m_waylandOutputDevice->create(); } -QSize AbstractOutput::orientateSize(const QSize &size) const +QSize AbstractWaylandOutput::orientateSize(const QSize &size) const { if (m_orientation == Qt::PortraitOrientation || m_orientation == Qt::InvertedPortraitOrientation) { return size.transposed(); diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -259,48 +259,6 @@ 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 --- 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.h b/plugins/platforms/drm/drm_output.h --- a/plugins/platforms/drm/drm_output.h +++ b/plugins/platforms/drm/drm_output.h @@ -20,7 +20,7 @@ #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 DrmConnector; class DrmCrtc; -class KWIN_EXPORT DrmOutput : public AbstractOutput +class KWIN_EXPORT DrmOutput : public AbstractWaylandOutput { Q_OBJECT public: diff --git a/plugins/platforms/drm/drm_output.cpp b/plugins/platforms/drm/drm_output.cpp --- a/plugins/platforms/drm/drm_output.cpp +++ b/plugins/platforms/drm/drm_output.cpp @@ -51,7 +51,7 @@ { DrmOutput::DrmOutput(DrmBackend *backend) - : AbstractOutput(backend) + : AbstractWaylandOutput(backend) , m_backend(backend) { } @@ -180,7 +180,7 @@ { 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 @@ 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 @@ 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/fbdev/fb_backend.h b/plugins/platforms/fbdev/fb_backend.h --- a/plugins/platforms/fbdev/fb_backend.h +++ b/plugins/platforms/fbdev/fb_backend.h @@ -20,21 +20,21 @@ *********************************************************************/ #ifndef KWIN_FB_BACKEND_H #define KWIN_FB_BACKEND_H -#include "abstract_output.h" +#include "abstract_wayland_output.h" #include "platform.h" #include #include 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 @@ } void setRawPhysicalSize(const QSize &set) { - AbstractOutput::setRawPhysicalSize(set); + AbstractWaylandOutput::setRawPhysicalSize(set); } private: diff --git a/plugins/platforms/hwcomposer/hwcomposer_backend.h b/plugins/platforms/hwcomposer/hwcomposer_backend.h --- a/plugins/platforms/hwcomposer/hwcomposer_backend.h +++ b/plugins/platforms/hwcomposer/hwcomposer_backend.h @@ -20,7 +20,7 @@ #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 @@ class HwcomposerWindow; class BacklightInputEventFilter; -class HwcomposerOutput : public AbstractOutput +class HwcomposerOutput : public AbstractWaylandOutput { Q_OBJECT public: diff --git a/plugins/platforms/hwcomposer/hwcomposer_backend.cpp b/plugins/platforms/hwcomposer/hwcomposer_backend.cpp --- a/plugins/platforms/hwcomposer/hwcomposer_backend.cpp +++ b/plugins/platforms/hwcomposer/hwcomposer_backend.cpp @@ -482,7 +482,7 @@ } HwcomposerOutput::HwcomposerOutput(hwc_composer_device_1_t *device) - : AbstractOutput() + : AbstractWaylandOutput() , m_device(device) { uint32_t configs[5]; diff --git a/plugins/platforms/virtual/virtual_output.h b/plugins/platforms/virtual/virtual_output.h --- a/plugins/platforms/virtual/virtual_output.h +++ b/plugins/platforms/virtual/virtual_output.h @@ -20,16 +20,16 @@ #ifndef KWIN_VIRTUAL_OUTPUT_H #define KWIN_VIRTUAL_OUTPUT_H -#include "abstract_output.h" +#include "abstract_wayland_output.h" #include #include namespace KWin { class VirtualBackend; -class VirtualOutput : public AbstractOutput +class VirtualOutput : public AbstractWaylandOutput { Q_OBJECT diff --git a/plugins/platforms/virtual/virtual_output.cpp b/plugins/platforms/virtual/virtual_output.cpp --- a/plugins/platforms/virtual/virtual_output.cpp +++ b/plugins/platforms/virtual/virtual_output.cpp @@ -23,7 +23,7 @@ { VirtualOutput::VirtualOutput(QObject *parent) - : AbstractOutput() + : AbstractWaylandOutput() { Q_UNUSED(parent); diff --git a/plugins/platforms/wayland/wayland_output.h b/plugins/platforms/wayland/wayland_output.h --- a/plugins/platforms/wayland/wayland_output.h +++ b/plugins/platforms/wayland/wayland_output.h @@ -20,7 +20,7 @@ #ifndef KWIN_WAYLAND_OUTPUT_H #define KWIN_WAYLAND_OUTPUT_H -#include "abstract_output.h" +#include "abstract_wayland_output.h" #include @@ -46,7 +46,7 @@ { class WaylandBackend; -class WaylandOutput : public AbstractOutput +class WaylandOutput : public AbstractWaylandOutput { Q_OBJECT public: diff --git a/plugins/platforms/wayland/wayland_output.cpp b/plugins/platforms/wayland/wayland_output.cpp --- a/plugins/platforms/wayland/wayland_output.cpp +++ b/plugins/platforms/wayland/wayland_output.cpp @@ -39,7 +39,7 @@ 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/x11/standalone/CMakeLists.txt b/plugins/platforms/x11/standalone/CMakeLists.txt --- a/plugins/platforms/x11/standalone/CMakeLists.txt +++ b/plugins/platforms/x11/standalone/CMakeLists.txt @@ -3,6 +3,7 @@ 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.h b/plugins/platforms/x11/standalone/screens_xrandr.h --- a/plugins/platforms/x11/standalone/screens_xrandr.h +++ b/plugins/platforms/x11/standalone/screens_xrandr.h @@ -20,40 +20,30 @@ #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/screens_xrandr.cpp b/plugins/platforms/x11/standalone/screens_xrandr.cpp --- a/plugins/platforms/x11/standalone/screens_xrandr.cpp +++ b/plugins/platforms/x11/standalone/screens_xrandr.cpp @@ -18,6 +18,8 @@ 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 @@ 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/virtual/virtual_output.h b/plugins/platforms/x11/standalone/x11_output.h copy from plugins/platforms/virtual/virtual_output.h copy to plugins/platforms/x11/standalone/x11_output.h --- a/plugins/platforms/virtual/virtual_output.h +++ b/plugins/platforms/x11/standalone/x11_output.h @@ -2,7 +2,7 @@ KWin - the KDE window manager This file is part of the KDE project. -Copyright (C) 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,46 +17,46 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ -#ifndef KWIN_VIRTUAL_OUTPUT_H -#define KWIN_VIRTUAL_OUTPUT_H +#ifndef KWIN_X11_OUTPUT_H +#define KWIN_X11_OUTPUT_H #include "abstract_output.h" +#include #include #include namespace KWin { -class VirtualBackend; -class VirtualOutput : public AbstractOutput +/** + * X11 output representation + **/ +class KWIN_EXPORT X11Output : public AbstractOutput { Q_OBJECT - public: - VirtualOutput(QObject *parent = nullptr); - virtual ~VirtualOutput(); - - QSize pixelSize() const override; - - void setGeometry(const QRect &geo); - - int getGammaRampSize() const override { - return m_gammaSize; - } - bool setGammaRamp(const ColorCorrect::GammaRamp &gamma) override { - Q_UNUSED(gamma); - return m_gammaResult; - } + 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: - Q_DISABLE_COPY(VirtualOutput); - friend class VirtualBackend; - - QSize m_pixelSize; - - int m_gammaSize = 200; - bool m_gammaResult = true; + QString m_name; + QRect m_geometry; + int m_refreshRate; }; } diff --git a/plugins/platforms/virtual/virtual_output.cpp b/plugins/platforms/x11/standalone/x11_output.cpp copy from plugins/platforms/virtual/virtual_output.cpp copy to plugins/platforms/x11/standalone/x11_output.cpp --- a/plugins/platforms/virtual/virtual_output.cpp +++ b/plugins/platforms/x11/standalone/x11_output.cpp @@ -2,7 +2,7 @@ KWin - the KDE window manager This file is part of the KDE project. -Copyright (C) 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,48 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . *********************************************************************/ -#include "virtual_output.h" +#include "x11_output.h" +#include "screens.h" namespace KWin { -VirtualOutput::VirtualOutput(QObject *parent) - : AbstractOutput() +X11Output::X11Output(QObject *parent) + : AbstractOutput(parent) { - Q_UNUSED(parent); +} + +QString X11Output::name() const +{ + return m_name; +} - setScale(1.); +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 } -VirtualOutput::~VirtualOutput() +void X11Output::setGeometry(QRect set) { + m_geometry = set; } -QSize VirtualOutput::pixelSize() const +int X11Output::refreshRate() const { - return m_pixelSize; + return m_refreshRate; } -void VirtualOutput::setGeometry(const QRect &geo) +void X11Output::setRefreshRate(int set) { - m_pixelSize = geo.size(); - setRawPhysicalSize(m_pixelSize); - setGlobalPos(geo.topLeft()); + m_refreshRate = set; } } diff --git a/plugins/platforms/x11/standalone/x11_platform.h b/plugins/platforms/x11/standalone/x11_platform.h --- a/plugins/platforms/x11/standalone/x11_platform.h +++ b/plugins/platforms/x11/standalone/x11_platform.h @@ -33,6 +33,7 @@ class XInputIntegration; class WindowSelector; class X11EventFilter; +class X11Output; class KWIN_EXPORT X11StandalonePlatform : public Platform { @@ -71,6 +72,12 @@ 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,14 +94,18 @@ **/ static bool hasGlx(); + template + void doUpdateOutputs(); + XInputIntegration *m_xinputIntegration = nullptr; QThread *m_openGLFreezeProtectionThread = nullptr; QTimer *m_openGLFreezeProtection = nullptr; Display *m_x11Display; QScopedPointer m_windowSelector; QScopedPointer m_screenEdgesFilter; std::unique_ptr m_syncFilter; + QVector m_outputs; }; } diff --git a/plugins/platforms/x11/standalone/x11_platform.cpp b/plugins/platforms/x11/standalone/x11_platform.cpp --- a/plugins/platforms/x11/standalone/x11_platform.cpp +++ b/plugins/platforms/x11/standalone/x11_platform.cpp @@ -35,13 +35,15 @@ #include "eglonxbackend.h" #include "keyboard_input.h" #include "logging.h" +#include "x11_output.h" #include "screens_xrandr.h" #include "screenedges_filter.h" #include "options.h" #include "overlaywindow_x11.h" #include "non_composited_outline.h" #include "workspace.h" #include "x11_decoration_renderer.h" +#include "xcbutils.h" #include @@ -106,7 +108,7 @@ Screens *X11StandalonePlatform::createScreens(QObject *parent) { - return new XRandRScreens(parent); + return new XRandRScreens(this, parent); } OpenGLBackend *X11StandalonePlatform::createOpenGLBackend() @@ -439,4 +441,109 @@ 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; +} + }