diff --git a/plugins/platforms/drm/drm_backend.cpp b/plugins/platforms/drm/drm_backend.cpp index def953b709..bb35b6dece 100644 --- a/plugins/platforms/drm/drm_backend.cpp +++ b/plugins/platforms/drm/drm_backend.cpp @@ -127,7 +127,7 @@ void DrmBackend::outputWentOff() void DrmBackend::turnOutputsOn() { m_dpmsFilter.reset(); - for (auto it = m_outputs.constBegin(), end = m_outputs.constEnd(); it != end; it++) { + for (auto it = m_enabledOutputs.constBegin(), end = m_enabledOutputs.constEnd(); it != end; it++) { (*it)->setDpms(DrmOutput::DpmsMode::On); } } @@ -138,7 +138,7 @@ void DrmBackend::checkOutputsAreOn() // already disabled, all outputs are on return; } - for (auto it = m_outputs.constBegin(), end = m_outputs.constEnd(); it != end; it++) { + for (auto it = m_enabledOutputs.constBegin(), end = m_enabledOutputs.constEnd(); it != end; it++) { if (!(*it)->isDpmsEnabled()) { // dpms still disabled, need to keep the filter return; @@ -396,6 +396,7 @@ void DrmBackend::updateOutputs() } DrmOutput *removed = *it; it = m_outputs.erase(it); + m_enabledOutputs.removeOne(removed); emit outputRemoved(removed); delete removed; } @@ -475,6 +476,7 @@ void DrmBackend::updateOutputs() } std::sort(connectedOutputs.begin(), connectedOutputs.end(), [] (DrmOutput *a, DrmOutput *b) { return a->m_conn->id() < b->m_conn->id(); }); m_outputs = connectedOutputs; + m_enabledOutputs = connectedOutputs; readOutputsConfiguration(); if (!m_outputs.isEmpty()) { emit screensQueried(); @@ -518,18 +520,50 @@ QByteArray DrmBackend::generateOutputConfigurationUuid() const void DrmBackend::configurationChangeRequested(KWayland::Server::OutputConfigurationInterface *config) { const auto changes = config->changes(); - for (auto it = changes.begin(); it != changes.end(); it++) { + bool countChanged = false; + //process all non-disabling changes + for (auto it = changes.begin(); it != changes.end(); it++) { KWayland::Server::OutputChangeSet *changeset = it.value(); auto drmoutput = findOutput(it.key()->uuid()); if (drmoutput == nullptr) { qCWarning(KWIN_DRM) << "Could NOT find DrmOutput matching " << it.key()->uuid(); - return; + continue; + } + if (changeset->enabledChanged() && changeset->enabled() == KWayland::Server::OutputDeviceInterface::Enablement::Enabled) { + drmoutput->setEnabled(true); + m_enabledOutputs << drmoutput; + emit outputAdded(drmoutput); + countChanged = true; } drmoutput->setChanges(changeset); } - emit screens()->changed(); + //process any disable requests + for (auto it = changes.begin(); it != changes.end(); it++) { + KWayland::Server::OutputChangeSet *changeset = it.value(); + if (changeset->enabledChanged() && changeset->enabled() == KWayland::Server::OutputDeviceInterface::Enablement::Disabled) { + if (m_enabledOutputs.count() == 1) { + qCWarning(KWIN_DRM) << "Not disabling final screen" << it.key()->uuid(); + continue; + } + auto drmoutput = findOutput(it.key()->uuid()); + if (drmoutput == nullptr) { + qCWarning(KWIN_DRM) << "Could NOT find DrmOutput matching " << it.key()->uuid(); + continue; + } + drmoutput->setEnabled(false); + m_enabledOutputs.removeOne(drmoutput); + emit outputRemoved(drmoutput); + countChanged = true; + } + } + + if (countChanged) { + emit screensQueried(); + } else { + emit screens()->changed(); + } // KCoreAddons needs kwayland's 2b3f9509ac1 to not crash if (KCoreAddons::version() >= QT_VERSION_CHECK(5, 39, 0)) { config->setApplied(); @@ -706,11 +740,11 @@ DrmSurfaceBuffer *DrmBackend::createBuffer(const std::shared_ptr &su void DrmBackend::outputDpmsChanged() { - if (m_outputs.isEmpty()) { + if (m_enabledOutputs.isEmpty()) { return; } bool enabled = false; - for (auto it = m_outputs.constBegin(); it != m_outputs.constEnd(); ++it) { + for (auto it = m_enabledOutputs.constBegin(); it != m_enabledOutputs.constEnd(); ++it) { enabled = enabled || (*it)->isDpmsEnabled(); } setOutputsEnabled(enabled); diff --git a/plugins/platforms/drm/drm_backend.h b/plugins/platforms/drm/drm_backend.h index 2549372f89..19b53139a0 100644 --- a/plugins/platforms/drm/drm_backend.h +++ b/plugins/platforms/drm/drm_backend.h @@ -92,6 +92,9 @@ public: QVector outputs() const { return m_outputs; } + QVector enabledOutputs() const { + return m_enabledOutputs; + } QVector planes() const { return m_planes; } @@ -126,7 +129,13 @@ public Q_SLOTS: void turnOutputsOn(); Q_SIGNALS: + /** + * Emitted whenever an output is removed/disabled + */ void outputRemoved(KWin::DrmOutput *output); + /** + * Emitted whenever an output is added/enabled + */ void outputAdded(KWin::DrmOutput *output); protected: @@ -158,8 +167,11 @@ private: QVector m_crtcs; // all connectors QVector m_connectors; - // currently active output pipelines (planes + crtc + encoder + connector) + // active output pipelines (planes + crtc + encoder + connector) QVector m_outputs; + // active and enabled pipelines (above + wl_output) + QVector m_enabledOutputs; + bool m_deleteBufferAfterPageFlip; bool m_atomicModeSetting = false; bool m_cursorEnabled = false; diff --git a/plugins/platforms/drm/drm_output.cpp b/plugins/platforms/drm/drm_output.cpp index 4527e842cf..8a21bf56df 100644 --- a/plugins/platforms/drm/drm_output.cpp +++ b/plugins/platforms/drm/drm_output.cpp @@ -183,6 +183,26 @@ qreal DrmOutput::scale() const return m_scale; } +void DrmOutput::setEnabled(bool enabled) +{ + if (enabled == isEnabled()) { + return; + } + if (enabled) { + setDpms(DpmsMode::On); + initOutput(); + } else { + setDpms(DpmsMode::Off); + delete m_waylandOutput.data(); + } + m_waylandOutputDevice->setEnabled(enabled ? + KWayland::Server::OutputDeviceInterface::Enablement::Enabled : KWayland::Server::OutputDeviceInterface::Enablement::Disabled); +} + +bool DrmOutput::isEnabled() const +{ + return !m_waylandOutput.isNull(); +} static KWayland::Server::OutputInterface::DpmsMode toWaylandDpmsMode(DrmOutput::DpmsMode mode) { @@ -272,8 +292,6 @@ bool DrmOutput::init(drmModeConnector *connector) m_internal = connector->connector_type == DRM_MODE_CONNECTOR_LVDS || connector->connector_type == DRM_MODE_CONNECTOR_eDP; - setDpms(DpmsMode::On); - if (m_internal) { connect(kwinApp(), &Application::screensCreated, this, [this] { @@ -297,8 +315,8 @@ bool DrmOutput::init(drmModeConnector *connector) m_physicalSize = physicalSize; initOutputDevice(connector); - initOutput(); + setEnabled(true); return true; } @@ -784,18 +802,13 @@ void DrmOutput::setChanges(KWayland::Server::OutputChangeSet *changes) bool DrmOutput::commitChanges() { Q_ASSERT(!m_waylandOutputDevice.isNull()); - Q_ASSERT(!m_waylandOutput.isNull()); if (m_changeset.isNull()) { qCDebug(KWIN_DRM) << "no changes"; // No changes to an output is an entirely valid thing return true; } - - if (m_changeset->enabledChanged()) { - qCDebug(KWIN_DRM) << "Setting enabled:"; - m_waylandOutputDevice->setEnabled(m_changeset->enabled()); - } + //enabledChanged is handled by drmbackend if (m_changeset->modeChanged()) { qCDebug(KWIN_DRM) << "Setting new mode:" << m_changeset->mode(); m_waylandOutputDevice->setCurrentMode(m_changeset->mode()); diff --git a/plugins/platforms/drm/drm_output.h b/plugins/platforms/drm/drm_output.h index 3e9149f422..ad8c21d2d8 100644 --- a/plugins/platforms/drm/drm_output.h +++ b/plugins/platforms/drm/drm_output.h @@ -75,6 +75,15 @@ public: bool present(DrmBuffer *buffer); void pageFlipped(); + /** + * Enable or disable the output. + * This differs from setDpms as it also + * removes the wl_output + * The default is on + */ + void setEnabled(bool enabled); + bool isEnabled() const; + /** * This sets the changes and tests them against the DRM output */ diff --git a/plugins/platforms/drm/screens_drm.cpp b/plugins/platforms/drm/screens_drm.cpp index b516ffca69..c2fbedfac9 100644 --- a/plugins/platforms/drm/screens_drm.cpp +++ b/plugins/platforms/drm/screens_drm.cpp @@ -43,7 +43,7 @@ void DrmScreens::init() QRect DrmScreens::geometry(int screen) const { - const auto outputs = m_backend->outputs(); + const auto outputs = m_backend->enabledOutputs(); if (screen >= outputs.size()) { return QRect(); } @@ -52,7 +52,7 @@ QRect DrmScreens::geometry(int screen) const qreal DrmScreens::scale(int screen) const { - const auto outputs = m_backend->outputs(); + const auto outputs = m_backend->enabledOutputs(); if (screen >= outputs.size()) { return 1; } @@ -61,7 +61,7 @@ qreal DrmScreens::scale(int screen) const QSize DrmScreens::size(int screen) const { - const auto outputs = m_backend->outputs(); + const auto outputs = m_backend->enabledOutputs(); if (screen >= outputs.size()) { return QSize(); } @@ -70,14 +70,14 @@ QSize DrmScreens::size(int screen) const void DrmScreens::updateCount() { - setCount(m_backend->outputs().size()); + setCount(m_backend->enabledOutputs().size()); } int DrmScreens::number(const QPoint &pos) const { int bestScreen = 0; int minDistance = INT_MAX; - const auto outputs = m_backend->outputs(); + const auto outputs = m_backend->enabledOutputs(); for (int i = 0; i < outputs.size(); ++i) { const QRect &geo = outputs.at(i)->geometry(); if (geo.contains(pos)) { @@ -97,7 +97,7 @@ int DrmScreens::number(const QPoint &pos) const QString DrmScreens::name(int screen) const { - const auto outputs = m_backend->outputs(); + const auto outputs = m_backend->enabledOutputs(); if (screen >= outputs.size()) { return Screens::name(screen); } @@ -106,7 +106,7 @@ QString DrmScreens::name(int screen) const float DrmScreens::refreshRate(int screen) const { - const auto outputs = m_backend->outputs(); + const auto outputs = m_backend->enabledOutputs(); if (screen >= outputs.size()) { return Screens::refreshRate(screen); } @@ -115,7 +115,7 @@ float DrmScreens::refreshRate(int screen) const QSizeF DrmScreens::physicalSize(int screen) const { - const auto outputs = m_backend->outputs(); + const auto outputs = m_backend->enabledOutputs(); if (screen >= outputs.size()) { return Screens::physicalSize(screen); } @@ -124,7 +124,7 @@ QSizeF DrmScreens::physicalSize(int screen) const bool DrmScreens::isInternal(int screen) const { - const auto outputs = m_backend->outputs(); + const auto outputs = m_backend->enabledOutputs(); if (screen >= outputs.size()) { return false; } @@ -133,7 +133,7 @@ bool DrmScreens::isInternal(int screen) const bool DrmScreens::supportsTransformations(int screen) const { - const auto outputs = m_backend->outputs(); + const auto outputs = m_backend->enabledOutputs(); if (screen >= outputs.size()) { return false; }