From 57b11f84296b69944b907a89d403dc50d36a75dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Mon, 15 Feb 2016 15:40:25 +0100 Subject: [PATCH] [backends/drm] Use an InputEventFilter to reenable outputs So far the DrmOutput connected to all input events when going into power saving. As we now have the input filters it's better to just install a filter when an output goes into powersave and remove the input filter again when all outputs are enabled again. To make this work InputRedirection gains a new method to add a new filter as the first filter. This is a potentially dangerous method as it allows to have a filter before LockScreenFilter gets the events. But in case of DPMS it's something we actually want. A nice new feature possible with the input filter is that we can filter out the event which re-enables the outputs. Thus when getting on a system with output off and screen locked, the first key hit doesn't go to the lock screen. Reviewed-By: Bhushan Shah BUG: 341201 Fixed-in: 5.6.0 (Wayland-only) --- backends/drm/drm_backend.cpp | 90 ++++++++++++++++++++++++++++++------ backends/drm/drm_backend.h | 26 ++++++++++- input.cpp | 5 ++ input.h | 10 +++- 4 files changed, 115 insertions(+), 16 deletions(-) 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();