diff --git a/backends/drm/drm_backend.cpp b/backends/drm/drm_backend.cpp index add296badb..06cc0d1274 100644 --- a/backends/drm/drm_backend.cpp +++ b/backends/drm/drm_backend.cpp @@ -20,7 +20,6 @@ along with this program. If not, see . #include "drm_backend.h" #include "composite.h" #include "cursor.h" -#include "input.h" #include "logging.h" #include "logind.h" #include "scene_qpainter_drm_backend.h" @@ -64,10 +63,47 @@ along with this program. If not, see . namespace KWin { +DpmsInputEventFilter::DpmsInputEventFilter(DrmBackend *backend) + : InputEventFilter() + , m_backend(backend) +{ +} + +DpmsInputEventFilter::~DpmsInputEventFilter() = default; + +bool DpmsInputEventFilter::pointerEvent(QMouseEvent *event, quint32 nativeButton) +{ + Q_UNUSED(event) + Q_UNUSED(nativeButton) + notify(); + return true; +} + +bool DpmsInputEventFilter::wheelEvent(QWheelEvent *event) +{ + Q_UNUSED(event) + notify(); + return true; +} + +bool DpmsInputEventFilter::keyEvent(QKeyEvent *event) +{ + Q_UNUSED(event) + notify(); + return true; +} + +void DpmsInputEventFilter::notify() +{ + // queued to not modify the list of event filters while filtering + QMetaObject::invokeMethod(m_backend, "turnOututsOn", Qt::QueuedConnection); +} + DrmBackend::DrmBackend(QObject *parent) : AbstractBackend(parent) , m_udev(new Udev) , m_udevMonitor(m_udev->monitor()) + , m_dpmsFilter() { handleOutputs(); m_cursor[0] = nullptr; @@ -108,6 +144,40 @@ void DrmBackend::init() connect(v, &VirtualTerminal::activeChanged, this, &DrmBackend::activate); } +void DrmBackend::outputWentOff() +{ + if (!m_dpmsFilter.isNull()) { + // already another output is off + return; + } + m_dpmsFilter.reset(new DpmsInputEventFilter(this)); + input()->prepandInputEventFilter(m_dpmsFilter.data()); +} + +void DrmBackend::turnOututsOn() +{ + m_dpmsFilter.reset(); + for (auto it = m_outputs.constBegin(), end = m_outputs.constEnd(); it != end; it++) { + (*it)->setDpms(DrmOutput::DpmsMode::On); + } +} + +void DrmBackend::checkOutputsAreOn() +{ + if (m_dpmsFilter.isNull()) { + // already disabled, all outputs are on + return; + } + for (auto it = m_outputs.constBegin(), end = m_outputs.constEnd(); it != end; it++) { + if (!(*it)->isDpmsEnabled()) { + // dpms still disabled, need to keep the filter + return; + } + } + // all outputs are on, disable the filter + m_dpmsFilter.reset(); +} + void DrmBackend::activate(bool active) { if (active) { @@ -996,6 +1066,9 @@ void DrmOutput::setDpms(DrmOutput::DpmsMode mode) if (m_dpms.isNull()) { return; } + if (mode == m_dpmsMode) { + return; + } if (drmModeConnectorSetProperty(m_backend->fd(), m_connector, m_dpms->prop_id, uint64_t(mode)) != 0) { qCWarning(KWIN_DRM) << "Setting DPMS failed"; return; @@ -1006,15 +1079,9 @@ void DrmOutput::setDpms(DrmOutput::DpmsMode mode) } emit dpmsChanged(); if (m_dpmsMode != DpmsMode::On) { - connect(input(), &InputRedirection::globalPointerChanged, this, &DrmOutput::reenableDpms); - connect(input(), &InputRedirection::pointerButtonStateChanged, this, &DrmOutput::reenableDpms); - connect(input(), &InputRedirection::pointerAxisChanged, this, &DrmOutput::reenableDpms); - connect(input(), &InputRedirection::keyStateChanged, this, &DrmOutput::reenableDpms); + m_backend->outputWentOff(); } else { - disconnect(input(), &InputRedirection::globalPointerChanged, this, &DrmOutput::reenableDpms); - disconnect(input(), &InputRedirection::pointerButtonStateChanged, this, &DrmOutput::reenableDpms); - disconnect(input(), &InputRedirection::pointerAxisChanged, this, &DrmOutput::reenableDpms); - disconnect(input(), &InputRedirection::keyStateChanged, this, &DrmOutput::reenableDpms); + m_backend->checkOutputsAreOn(); blank(); if (Compositor *compositor = Compositor::self()) { compositor->addRepaintFull(); @@ -1022,11 +1089,6 @@ void DrmOutput::setDpms(DrmOutput::DpmsMode mode) } } -void DrmOutput::reenableDpms() -{ - setDpms(DpmsMode::On); -} - QString DrmOutput::name() const { if (!m_waylandOutput) { diff --git a/backends/drm/drm_backend.h b/backends/drm/drm_backend.h index b247776b72..daa3167cfe 100644 --- a/backends/drm/drm_backend.h +++ b/backends/drm/drm_backend.h @@ -20,6 +20,7 @@ along with this program. If not, see . #ifndef KWIN_DRM_BACKEND_H #define KWIN_DRM_BACKEND_H #include "abstract_backend.h" +#include "input.h" #include #include @@ -45,6 +46,7 @@ class UdevMonitor; class DrmBuffer; class DrmOutput; +class DpmsInputEventFilter; template struct DrmCleanup @@ -88,6 +90,13 @@ public: } void bufferDestroyed(DrmBuffer *b); + void outputWentOff(); + void checkOutputsAreOn(); + +public Q_SLOTS: + void turnOututsOn(); + + Q_SIGNALS: void outputRemoved(KWin::DrmOutput *output); void outputAdded(KWin::DrmOutput *output); @@ -120,6 +129,7 @@ private: int m_pageFlipsPending = 0; bool m_active = false; QVector m_buffers; + QScopedPointer m_dpmsFilter; }; class DrmOutput : public QObject @@ -172,7 +182,6 @@ private: void initEdid(drmModeConnector *connector); void initDpms(drmModeConnector *connector); bool isCurrentMode(const drmModeModeInfo *mode) const; - void reenableDpms(); void initUuid(); void setGlobalPos(const QPoint &pos); @@ -243,6 +252,21 @@ private: QImage *m_image = nullptr; }; +class DpmsInputEventFilter : public InputEventFilter +{ +public: + DpmsInputEventFilter(DrmBackend *backend); + ~DpmsInputEventFilter(); + + bool pointerEvent(QMouseEvent *event, quint32 nativeButton) override; + bool wheelEvent(QWheelEvent *event) override; + bool keyEvent(QKeyEvent *event) override; + +private: + void notify(); + DrmBackend *m_backend; +}; + } Q_DECLARE_METATYPE(KWin::DrmOutput*) diff --git a/input.cpp b/input.cpp index 97af259e7c..18aa8203a6 100644 --- a/input.cpp +++ b/input.cpp @@ -626,6 +626,11 @@ void InputRedirection::installInputEventFilter(InputEventFilter *filter) m_filters << filter; } +void InputRedirection::prepandInputEventFilter(InputEventFilter *filter) +{ + m_filters.prepend(filter); +} + void InputRedirection::uninstallInputEventFilter(InputEventFilter *filter) { m_filters.removeAll(filter); diff --git a/input.h b/input.h index 466b9bee01..1de4d9f9a0 100644 --- a/input.h +++ b/input.h @@ -128,6 +128,14 @@ public: bool supportsPointerWarping() const; void warpPointer(const QPointF &pos); + /** + * Adds the @p filter to the list of event filters and makes it the first + * event filter in processing. + * + * Note: the event filter will get events before the lock screen can get them, thus + * this is a security relevant method. + **/ + void prepandInputEventFilter(InputEventFilter *filter); void uninstallInputEventFilter(InputEventFilter *filter); Toplevel *findToplevel(const QPoint &pos); GlobalShortcutsManager *shortcuts() const { @@ -230,7 +238,7 @@ private: * Deleting an instance of InputEventFilter automatically uninstalls it from * InputRedirection. **/ -class InputEventFilter +class KWIN_EXPORT InputEventFilter { public: InputEventFilter();