diff --git a/abstract_wayland_output.cpp b/abstract_wayland_output.cpp index b4ce31ca1..30427e170 100644 --- a/abstract_wayland_output.cpp +++ b/abstract_wayland_output.cpp @@ -1,347 +1,352 @@ /******************************************************************** 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 "screens.h" #include "wayland_server.h" // KWayland #include #include #include // KF5 #include #include namespace KWin { AbstractWaylandOutput::AbstractWaylandOutput(QObject *parent) : AbstractOutput(parent) { } AbstractWaylandOutput::~AbstractWaylandOutput() { delete m_xdgOutput.data(); delete m_waylandOutput.data(); delete m_waylandOutputDevice.data(); } QString AbstractWaylandOutput::name() const { return QStringLiteral("%1 %2").arg(m_waylandOutputDevice->manufacturer()).arg( m_waylandOutputDevice->model()); } QByteArray AbstractWaylandOutput::uuid() const { return m_waylandOutputDevice->uuid(); } QRect AbstractWaylandOutput::geometry() const { return QRect(globalPos(), pixelSize() / scale()); } QSize AbstractWaylandOutput::physicalSize() const { return orientateSize(m_waylandOutputDevice->physicalSize()); } int AbstractWaylandOutput::refreshRate() const { return m_waylandOutputDevice->refreshRate(); } QPoint AbstractWaylandOutput::globalPos() const { return m_waylandOutputDevice->globalPosition(); } void AbstractWaylandOutput::setGlobalPos(const QPoint &pos) { m_waylandOutputDevice->setGlobalPosition(pos); if (isEnabled()) { m_waylandOutput->setGlobalPosition(pos); m_xdgOutput->setLogicalPosition(pos); m_xdgOutput->done(); } } +QSize AbstractWaylandOutput::modeSize() const +{ + return m_waylandOutputDevice->pixelSize(); +} + QSize AbstractWaylandOutput::pixelSize() const { return orientateSize(m_waylandOutputDevice->pixelSize()); } qreal AbstractWaylandOutput::scale() const { return m_waylandOutputDevice->scaleF(); } void AbstractWaylandOutput::setScale(qreal scale) { m_waylandOutputDevice->setScaleF(scale); if (isEnabled()) { // 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)); m_xdgOutput->setLogicalSize(pixelSize() / scale); m_xdgOutput->done(); } } using DeviceInterface = KWayland::Server::OutputDeviceInterface; KWayland::Server::OutputInterface::Transform toOutputTransform(DeviceInterface::Transform transform) { using Transform = DeviceInterface::Transform; using OutputTransform = KWayland::Server::OutputInterface::Transform; switch (transform) { case Transform::Rotated90: return OutputTransform::Rotated90; case Transform::Rotated180: return OutputTransform::Rotated180; case Transform::Rotated270: return OutputTransform::Rotated270; case Transform::Flipped: return OutputTransform::Flipped; case Transform::Flipped90: return OutputTransform::Flipped90; case Transform::Flipped180: return OutputTransform::Flipped180; case Transform::Flipped270: return OutputTransform::Flipped270; default: return OutputTransform::Normal; } } void AbstractWaylandOutput::setTransform(DeviceInterface::Transform transform) { m_waylandOutputDevice->setTransform(transform); if (isEnabled()) { m_waylandOutput->setTransform(toOutputTransform(transform)); m_xdgOutput->setLogicalSize(pixelSize() / scale()); m_xdgOutput->done(); } } inline AbstractWaylandOutput::Transform toTransform(DeviceInterface::Transform deviceTransform) { return static_cast(deviceTransform); } inline DeviceInterface::Transform toDeviceTransform(AbstractWaylandOutput::Transform transform) { return static_cast(transform); } void AbstractWaylandOutput::applyChanges(const KWayland::Server::OutputChangeSet *changeSet) { qCDebug(KWIN_CORE) << "Apply changes to the Wayland output."; bool emitModeChanged = false; bool overallSizeCheckNeeded = false; // Enablement changes are handled by platform. if (changeSet->modeChanged()) { qCDebug(KWIN_CORE) << "Setting new mode:" << changeSet->mode(); m_waylandOutputDevice->setCurrentMode(changeSet->mode()); updateMode(changeSet->mode()); emitModeChanged = true; } if (changeSet->transformChanged()) { qCDebug(KWIN_CORE) << "Server setting transform: " << (int)(changeSet->transform()); updateTransform(toTransform(changeSet->transform())); setTransform(changeSet->transform()); emitModeChanged = true; } if (changeSet->positionChanged()) { qCDebug(KWIN_CORE) << "Server setting position: " << changeSet->position(); setGlobalPos(changeSet->position()); // may just work already! overallSizeCheckNeeded = true; } if (changeSet->scaleChanged()) { qCDebug(KWIN_CORE) << "Setting scale:" << changeSet->scale(); setScale(changeSet->scaleF()); emitModeChanged = true; } overallSizeCheckNeeded |= emitModeChanged; if (overallSizeCheckNeeded) { emit screens()->changed(); } if (emitModeChanged) { emit modeChanged(); } } bool AbstractWaylandOutput::isEnabled() const { return m_waylandOutputDevice->enabled() == DeviceInterface::Enablement::Enabled; } void AbstractWaylandOutput::setEnabled(bool enable) { if (enable == isEnabled()) { return; } if (enable) { waylandOutputDevice()->setEnabled(DeviceInterface::Enablement::Enabled); createWaylandOutput(); updateEnablement(true); } else { waylandOutputDevice()->setEnabled(DeviceInterface::Enablement::Disabled); // xdg-output is destroyed in KWayland on wl_output going away. delete m_waylandOutput.data(); updateEnablement(false); } } void AbstractWaylandOutput::setWaylandMode(const QSize &size, int refreshRate) { if (!isEnabled()) { return; } m_waylandOutput->setCurrentMode(size, refreshRate); m_xdgOutput->setLogicalSize(pixelSize() / scale()); m_xdgOutput->done(); } void AbstractWaylandOutput::createXdgOutput() { Q_ASSERT(!m_waylandOutput.isNull()); Q_ASSERT(m_xdgOutput.isNull()); m_xdgOutput = waylandServer()->xdgOutputManager()->createXdgOutput(m_waylandOutput, m_waylandOutput); m_xdgOutput->setLogicalSize(pixelSize() / scale()); m_xdgOutput->setLogicalPosition(globalPos()); m_xdgOutput->done(); } void AbstractWaylandOutput::createWaylandOutput() { Q_ASSERT(m_waylandOutput.isNull()); 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(m_waylandOutputDevice->physicalSize()); /* * add modes */ for(const auto &mode: m_waylandOutputDevice->modes()) { KWayland::Server::OutputInterface::ModeFlags flags; if (mode.flags & DeviceInterface::ModeFlag::Current) { flags |= KWayland::Server::OutputInterface::ModeFlag::Current; } if (mode.flags & DeviceInterface::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); } ); } void AbstractWaylandOutput::initInterfaces(const QString &model, const QString &manufacturer, const QByteArray &uuid, const QSize &physicalSize, const QVector &modes) { Q_ASSERT(m_waylandOutputDevice.isNull()); 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(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(); createWaylandOutput(); } QSize AbstractWaylandOutput::orientateSize(const QSize &size) const { using Transform = DeviceInterface::Transform; const Transform transform = m_waylandOutputDevice->transform(); if (transform == Transform::Rotated90 || transform == Transform::Rotated270 || transform == Transform::Flipped90 || transform == Transform::Flipped270) { return size.transposed(); } return size; } void AbstractWaylandOutput::setTransform(Transform transform) { const auto deviceTransform = toDeviceTransform(transform); if (deviceTransform == m_waylandOutputDevice->transform()) { return; } setTransform(deviceTransform); emit modeChanged(); } AbstractWaylandOutput::Transform AbstractWaylandOutput::transform() const { return static_cast(m_waylandOutputDevice->transform()); } } diff --git a/abstract_wayland_output.h b/abstract_wayland_output.h index 202c50fe6..c2d91508d 100644 --- a/abstract_wayland_output.h +++ b/abstract_wayland_output.h @@ -1,187 +1,191 @@ /******************************************************************** 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: enum class Transform { Normal, Rotated90, Rotated180, Rotated270, Flipped, Flipped90, Flipped180, Flipped270 }; explicit AbstractWaylandOutput(QObject *parent = nullptr); ~AbstractWaylandOutput() override; QString name() const override; QByteArray uuid() const override; + QSize modeSize() const; + + // TODO: The name is ambiguous. Rename this function. QSize pixelSize() const; + qreal scale() const override; /** * The geometry of this output in global compositor co-ordinates (i.e scaled) */ QRect geometry() const override; QSize physicalSize() const override; /** * 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); void applyChanges(const KWayland::Server::OutputChangeSet *changeSet) override; QPointer waylandOutput() const { return m_waylandOutput; } bool isEnabled() const; /** * Enable or disable the output. * * This differs from updateDpms as it also removes the wl_output. * The default is on. */ void setEnabled(bool enable) override; Q_SIGNALS: void modeChanged(); protected: void initInterfaces(const QString &model, const QString &manufacturer, const QByteArray &uuid, const QSize &physicalSize, const QVector &modes); QPointer xdgOutput() const { return m_xdgOutput; } QPointer waylandOutputDevice() const { return m_waylandOutputDevice; } QPoint globalPos() const; bool internal() const { return m_internal; } void setInternal(bool set) { m_internal = set; } void setDpmsSupported(bool set) { m_supportsDpms = set; } virtual void updateEnablement(bool enable) { Q_UNUSED(enable); } virtual void updateDpms(KWayland::Server::OutputInterface::DpmsMode mode) { Q_UNUSED(mode); } virtual void updateMode(int modeIndex) { Q_UNUSED(modeIndex); } virtual void updateTransform(Transform transform) { Q_UNUSED(transform); } void setWaylandMode(const QSize &size, int refreshRate); void setTransform(Transform transform); QSize orientateSize(const QSize &size) const; /** * Returns the orientation of this output. * * - Flipped along the vertical axis is landscape + inv. portrait. * - Rotated 90° and flipped along the horizontal axis is portrait + inv. landscape * - Rotated 180° and flipped along the vertical axis is inv. landscape + inv. portrait * - Rotated 270° and flipped along the horizontal axis is inv. portrait + inv. landscape + * portrait */ Transform transform() const; private: void createWaylandOutput(); void createXdgOutput(); void setTransform(KWayland::Server::OutputDeviceInterface::Transform transform); QPointer m_waylandOutput; QPointer m_xdgOutput; QPointer m_waylandOutputDevice; KWayland::Server::OutputInterface::DpmsMode m_dpms = KWayland::Server::OutputInterface::DpmsMode::On; bool m_internal = false; bool m_supportsDpms = false; }; } #endif // KWIN_OUTPUT_H