diff --git a/abstract_wayland_output.h b/abstract_wayland_output.h --- a/abstract_wayland_output.h +++ b/abstract_wayland_output.h @@ -92,6 +92,7 @@ return m_waylandOutput; } + bool isEnabled() const; /** * Enable or disable the output. * @@ -130,6 +131,10 @@ 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); } @@ -147,7 +152,6 @@ private: void createWaylandOutput(); void createXdgOutput(); - bool isEnabled() const; QPointer m_waylandOutput; QPointer m_xdgOutput; diff --git a/abstract_wayland_output.cpp b/abstract_wayland_output.cpp --- a/abstract_wayland_output.cpp +++ b/abstract_wayland_output.cpp @@ -158,15 +158,17 @@ if (enable == isEnabled()) { return; } + if (enable) { - updateDpms(KWayland::Server::OutputInterface::DpmsMode::On); + waylandOutputDevice()->setEnabled(DeviceInterface::Enablement::Enabled); createWaylandOutput(); + updateEnablement(true); } else { - updateDpms(KWayland::Server::OutputInterface::DpmsMode::Off); - delete waylandOutput().data(); + waylandOutputDevice()->setEnabled(DeviceInterface::Enablement::Disabled); + // xdg-output is destroyed in KWayland on wl_output going away. + delete m_waylandOutput.data(); + updateEnablement(false); } - waylandOutputDevice()->setEnabled(enable ? DeviceInterface::Enablement::Enabled : - DeviceInterface::Enablement::Disabled); } void AbstractWaylandOutput::setWaylandMode(const QSize &size, int refreshRate) @@ -227,7 +229,7 @@ connect(m_waylandOutput.data(), &KWayland::Server::OutputInterface::dpmsModeRequested, this, [this] (KWayland::Server::OutputInterface::DpmsMode mode) { updateDpms(mode); - }, Qt::QueuedConnection + } ); } diff --git a/platform.h b/platform.h --- a/platform.h +++ b/platform.h @@ -426,9 +426,7 @@ virtual Outputs enabledOutputs() const { return Outputs(); } - AbstractOutput *findOutput(const QByteArray &uuid); - virtual void enableOutput(AbstractOutput *output, bool enable); /** * A string of information to include in kwin debug output diff --git a/platform.cpp b/platform.cpp --- a/platform.cpp +++ b/platform.cpp @@ -151,7 +151,6 @@ if (changeset->enabledChanged() && changeset->enabled() == Enablement::Enabled) { output->setEnabled(true); - enableOutput(output, true); countChanged = true; } output->applyChanges(changeset); @@ -175,16 +174,10 @@ continue; } output->setEnabled(false); - enableOutput(output, false); countChanged = true; } } - - if (countChanged) { - emit screensQueried(); - } else { - emit screens()->changed(); - } + emit screens()->changed(); config->setApplied(); } @@ -201,12 +194,6 @@ return nullptr; } -void Platform::enableOutput(AbstractOutput *output, bool enable) -{ - Q_UNUSED(output) - Q_UNUSED(enable) -} - void Platform::setSoftWareCursor(bool set) { if (qEnvironmentVariableIsSet("KWIN_FORCE_SW_CURSOR")) { diff --git a/plugins/platforms/drm/drm_backend.h b/plugins/platforms/drm/drm_backend.h --- a/plugins/platforms/drm/drm_backend.h +++ b/plugins/platforms/drm/drm_backend.h @@ -89,16 +89,16 @@ return m_enabledOutputs; } - void enableOutput(AbstractOutput *output, bool enable) override; + void enableOutput(DrmOutput *output, bool enable); QVector planes() const { return m_planes; } QVector overlayPlanes() const { return m_overlayPlanes; } - void outputWentOff(); + void createDpmsFilter(); void checkOutputsAreOn(); // QPainter reuses buffers @@ -160,11 +160,11 @@ void updateCursor(); void moveCursor(); void initCursor(); - void outputDpmsChanged(); void readOutputsConfiguration(); void writeOutputsConfiguration(); QByteArray generateOutputConfigurationUuid() const; DrmOutput *findOutput(quint32 connector); + void updateOutputsEnabled(); QScopedPointer m_udev; QScopedPointer m_udevMonitor; int m_fd = -1; diff --git a/plugins/platforms/drm/drm_backend.cpp b/plugins/platforms/drm/drm_backend.cpp --- a/plugins/platforms/drm/drm_backend.cpp +++ b/plugins/platforms/drm/drm_backend.cpp @@ -142,7 +142,7 @@ return m_enabledOutputs; } -void DrmBackend::outputWentOff() +void DrmBackend::createDpmsFilter() { if (!m_dpmsFilter.isNull()) { // already another output is off @@ -199,7 +199,6 @@ DrmOutput *o = *it; // only relevant in atomic mode o->m_modesetRequested = true; - o->pageFlipped(); // TODO: Do we really need this? o->m_crtc->blank(); o->showCursor(); o->moveCursor(cp); @@ -237,17 +236,13 @@ Q_UNUSED(sec) Q_UNUSED(usec) auto output = reinterpret_cast(data); + output->pageFlipped(); output->m_backend->m_pageFlipsPending--; if (output->m_backend->m_pageFlipsPending == 0) { // TODO: improve, this currently means we wait for all page flips or all outputs. // It would be better to driver the repaint per output - if (output->m_dpmsAtomicOffPending) { - output->m_modesetRequested = true; - output->dpmsAtomicOff(); - } - if (Compositor::self()) { Compositor::self()->bufferSwapComplete(); } @@ -474,7 +469,6 @@ output->m_conn = con; crtc->setOutput(output); output->m_crtc = crtc; - connect(output, &DrmOutput::dpmsChanged, this, &DrmBackend::outputDpmsChanged); if (modeCrtc->mode_valid) { output->m_mode = modeCrtc->mode; @@ -562,16 +556,21 @@ return hash.result().toHex().left(10); } -void DrmBackend::enableOutput(AbstractOutput *output, bool enable) +void DrmBackend::enableOutput(DrmOutput *output, bool enable) { - auto *drmOutput = static_cast(output); if (enable) { - m_enabledOutputs << drmOutput; - emit outputAdded(drmOutput); + Q_ASSERT(!m_enabledOutputs.contains(output)); + m_enabledOutputs << output; + emit outputAdded(output); } else { - m_enabledOutputs.removeOne(drmOutput); - emit outputRemoved(drmOutput); + Q_ASSERT(m_enabledOutputs.contains(output)); + m_enabledOutputs.removeOne(output); + Q_ASSERT(!m_enabledOutputs.contains(output)); + emit outputRemoved(output); } + updateOutputsEnabled(); + checkOutputsAreOn(); + emit screensQueried(); } DrmOutput *DrmBackend::findOutput(quint32 connector) @@ -753,7 +752,7 @@ } #endif -void DrmBackend::outputDpmsChanged() +void DrmBackend::updateOutputsEnabled() { if (m_enabledOutputs.isEmpty()) { return; 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 @@ -83,9 +83,6 @@ bool supportsTransformations() const; -Q_SIGNALS: - void dpmsChanged(); - private: friend class DrmBackend; friend class DrmCrtc; // TODO: For use of setModeLegacy. Remove later when we allow multiple connectors per crtc @@ -111,9 +108,16 @@ bool initPrimaryPlane(); bool initCursorPlane(); - void dpmsOnHandler(); - void dpmsOffHandler(); + void atomicEnable(); + void atomicDisable(); + void updateEnablement(bool enable) override; + bool dpmsAtomicOff(); + bool dpmsLegacyApply(); + + void dpmsFinishOn(); + void dpmsFinishOff(); + bool atomicReqModesetPopulate(drmModeAtomicReq *req, bool enable); void updateDpms(KWayland::Server::OutputInterface::DpmsMode mode) override; void updateMode(int modeIndex) override; @@ -142,7 +146,7 @@ DrmPlane* m_cursorPlane = nullptr; QVector m_nextPlanesFlipList; bool m_pageFlipPending = false; - bool m_dpmsAtomicOffPending = false; + bool m_atomicOffPending = false; bool m_modesetRequested = true; struct { 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 @@ -470,6 +470,56 @@ } } +void DrmOutput::updateEnablement(bool enable) +{ + if (enable) { + m_dpmsModePending = DpmsMode::On; + if (m_backend->atomicModeSetting()) { + atomicEnable(); + } else { + if (dpmsLegacyApply()) { + m_backend->enableOutput(this, true); + } + } + + } else { + m_dpmsModePending = DpmsMode::Off; + if (m_backend->atomicModeSetting()) { + atomicDisable(); + } else { + if (dpmsLegacyApply()) { + m_backend->enableOutput(this, false); + } + } + } +} + +void DrmOutput::atomicEnable() +{ + m_modesetRequested = true; + + if (m_atomicOffPending) { + Q_ASSERT(m_pageFlipPending); + m_atomicOffPending = false; + } + m_backend->enableOutput(this, true); + + if (Compositor *compositor = Compositor::self()) { + compositor->addRepaintFull(); + } +} + +void DrmOutput::atomicDisable() +{ + m_modesetRequested = true; + + m_backend->enableOutput(this, false); + m_atomicOffPending = true; + if (!m_pageFlipPending) { + dpmsAtomicOff(); + } +} + static DrmOutput::DpmsMode fromWaylandDpmsMode(KWayland::Server::OutputInterface::DpmsMode wlMode) { using namespace KWayland::Server; @@ -506,11 +556,12 @@ void DrmOutput::updateDpms(KWayland::Server::OutputInterface::DpmsMode mode) { - if (m_dpms.isNull()) { + if (m_dpms.isNull() || !isEnabled()) { return; } const auto drmMode = fromWaylandDpmsMode(mode); + if (drmMode == m_dpmsModePending) { qCDebug(KWIN_DRM) << "New DPMS mode equals old mode. DPMS unchanged."; return; @@ -521,41 +572,30 @@ if (m_backend->atomicModeSetting()) { m_modesetRequested = true; if (drmMode == DpmsMode::On) { - if (m_pageFlipPending) { - m_pageFlipPending = false; - Compositor::self()->bufferSwapComplete(); + if (m_atomicOffPending) { + Q_ASSERT(m_pageFlipPending); + m_atomicOffPending = false; } - dpmsOnHandler(); + dpmsFinishOn(); } else { - m_dpmsAtomicOffPending = true; + m_atomicOffPending = true; if (!m_pageFlipPending) { dpmsAtomicOff(); } } } else { - if (drmModeConnectorSetProperty(m_backend->fd(), m_conn->id(), m_dpms->prop_id, uint64_t(drmMode)) < 0) { - m_dpmsModePending = m_dpmsMode; - qCWarning(KWIN_DRM) << "Setting DPMS failed"; - return; - } - if (drmMode == DpmsMode::On) { - dpmsOnHandler(); - } else { - dpmsOffHandler(); - } - m_dpmsMode = m_dpmsModePending; + dpmsLegacyApply(); } } -void DrmOutput::dpmsOnHandler() +void DrmOutput::dpmsFinishOn() { qCDebug(KWIN_DRM) << "DPMS mode set for output" << m_crtc->id() << "to On."; auto wlOutput = waylandOutput(); if (wlOutput) { - wlOutput->setDpmsMode(toWaylandDpmsMode(m_dpmsModePending)); + wlOutput->setDpmsMode(toWaylandDpmsMode(DpmsMode::On)); } - emit dpmsChanged(); m_backend->checkOutputsAreOn(); if (!m_backend->atomicModeSetting()) { @@ -566,17 +606,31 @@ } } -void DrmOutput::dpmsOffHandler() +void DrmOutput::dpmsFinishOff() { qCDebug(KWIN_DRM) << "DPMS mode set for output" << m_crtc->id() << "to Off."; - auto wlOutput = waylandOutput(); - if (wlOutput) { - wlOutput->setDpmsMode(toWaylandDpmsMode(m_dpmsModePending)); + if (isEnabled()) { + waylandOutput()->setDpmsMode(toWaylandDpmsMode(m_dpmsModePending)); + m_backend->createDpmsFilter(); } - emit dpmsChanged(); +} - m_backend->outputWentOff(); +bool DrmOutput::dpmsLegacyApply() +{ + if (drmModeConnectorSetProperty(m_backend->fd(), m_conn->id(), + m_dpms->prop_id, uint64_t(m_dpmsModePending)) < 0) { + m_dpmsModePending = m_dpmsMode; + qCWarning(KWIN_DRM) << "Setting DPMS failed"; + return false; + } + if (m_dpmsModePending == DpmsMode::On) { + dpmsFinishOn(); + } else { + dpmsFinishOff(); + } + m_dpmsMode = m_dpmsModePending; + return true; } void DrmOutput::transform(KWayland::Server::OutputDeviceInterface::Transform transform) @@ -682,6 +736,7 @@ void DrmOutput::pageFlipped() { + Q_ASSERT(m_pageFlipPending); m_pageFlipPending = false; if (m_deleted) { deleteLater(); @@ -729,10 +784,17 @@ } m_crtc->flipBuffer(); } + + if (m_atomicOffPending) { + dpmsAtomicOff(); + } } bool DrmOutput::present(DrmBuffer *buffer) { + if (m_dpmsModePending != DpmsMode::On) { + return false; + } if (m_backend->atomicModeSetting()) { return presentAtomically(buffer); } else { @@ -742,7 +804,7 @@ bool DrmOutput::dpmsAtomicOff() { - m_dpmsAtomicOffPending = false; + m_atomicOffPending = false; // TODO: With multiple planes: deactivate all of them here delete m_primaryPlane->next(); @@ -758,10 +820,9 @@ return false; } m_nextPlanesFlipList.clear(); - dpmsOffHandler(); + dpmsFinishOff(); return true; - } bool DrmOutput::presentAtomically(DrmBuffer *buffer) @@ -839,9 +900,6 @@ m_crtc->setNext(buffer); return false; } - if (m_dpmsMode != DpmsMode::On) { - return false; - } // Do we need to set a new mode first? if (!m_crtc->current() || m_crtc->current()->needsModeChange(buffer)) { @@ -885,7 +943,7 @@ qCWarning(KWIN_DRM) << "Setting DPMS failed"; m_dpmsModePending = m_dpmsMode; if (m_dpmsMode != DpmsMode::On) { - dpmsOffHandler(); + dpmsFinishOff(); } }