From 99caa5490180aede6a5f5dd4615274522ee64107 Mon Sep 17 00:00:00 2001 From: David Edmundson Date: Thu, 1 Aug 2024 08:46:06 +0000 Subject: [PATCH] core: Introduce explicit weight to InputEventFilter This is problematic as it means anything requiring a fixed position in the chain cannot be added or removed at runtime. This is bad for both performance and code cleanliness as all code ends up input.cpp rather than where it semantically belongs. Remaining users that prepend event filters have the problem of the order being effectively undefined. This patch adds a weight attribute to the filter allowing filters to be installed and removed at runtime whilst maintaining a specific deterministic order. Currently the order is defined by an enum based on the filter names. This gives an easy to read explicit order for anyone reading kwin code, but it is left cast to an int so we have future flexibility. --- src/dpmsinputeventfilter.cpp | 2 +- src/input.cpp | 72 +++++++++++++++++-- src/input.h | 57 ++++++++++++--- src/placeholderinputeventfilter.cpp | 5 ++ src/placeholderinputeventfilter.h | 1 + src/plugins/bouncekeys/bouncekeys.cpp | 5 +- .../buttonrebinds/buttonrebindsfilter.cpp | 5 +- src/plugins/eis/eisinputcapturefilter.cpp | 3 +- src/plugins/eis/eisinputcapturemanager.cpp | 2 +- src/plugins/stickykeys/stickykeys.cpp | 5 +- src/popup_input_filter.cpp | 1 + src/workspace.cpp | 4 +- 12 files changed, 134 insertions(+), 28 deletions(-) diff --git a/src/dpmsinputeventfilter.cpp b/src/dpmsinputeventfilter.cpp index 7dbaed8d0d..86d7b8754a 100644 --- a/src/dpmsinputeventfilter.cpp +++ b/src/dpmsinputeventfilter.cpp @@ -23,7 +23,7 @@ namespace KWin { DpmsInputEventFilter::DpmsInputEventFilter() - : InputEventFilter() + : InputEventFilter(InputFilterOrder::Dpms) { KSharedConfig::Ptr kwinSettings = kwinApp()->config(); m_enableDoubleTap = kwinSettings->group(QStringLiteral("Wayland")).readEntry("DoubleTapWakeup", true); diff --git a/src/input.cpp b/src/input.cpp index e26865ea8d..acba7d7d0d 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -101,7 +101,10 @@ static PointerAxisSource kwinAxisSourceToKWaylandAxisSource(InputRedirection::Po } } -InputEventFilter::InputEventFilter() = default; +InputEventFilter::InputEventFilter(InputFilterOrder::Order weight) + : m_weight(weight) +{ +} InputEventFilter::~InputEventFilter() { @@ -110,6 +113,11 @@ InputEventFilter::~InputEventFilter() } } +int InputEventFilter::weight() const +{ + return m_weight; +} + bool InputEventFilter::pointerEvent(MouseEvent *event, quint32 nativeButton) { return false; @@ -281,6 +289,10 @@ bool InputEventFilter::passToInputMethod(QKeyEvent *event) class VirtualTerminalFilter : public InputEventFilter { public: + VirtualTerminalFilter() + : InputEventFilter(InputFilterOrder::VirtualTerminal) + { + } bool keyEvent(KeyEvent *event) override { // really on press and not on release? X11 switches on press. @@ -298,6 +310,10 @@ public: class LockScreenFilter : public InputEventFilter { public: + LockScreenFilter() + : InputEventFilter(InputFilterOrder::LockScreen) + { + } bool pointerEvent(MouseEvent *event, quint32 nativeButton) override { if (!waylandServer()->isScreenLocked()) { @@ -511,6 +527,10 @@ private: class EffectsFilter : public InputEventFilter { public: + EffectsFilter() + : InputEventFilter(InputFilterOrder::Effects) + { + } bool pointerEvent(MouseEvent *event, quint32 nativeButton) override { if (!effects) { @@ -596,6 +616,10 @@ public: class MoveResizeFilter : public InputEventFilter { public: + MoveResizeFilter() + : InputEventFilter(InputFilterOrder::InteractiveMoveResize) + { + } bool pointerEvent(MouseEvent *event, quint32 nativeButton) override { Window *window = workspace()->moveResizeWindow(); @@ -706,6 +730,10 @@ private: class WindowSelectorFilter : public InputEventFilter { public: + WindowSelectorFilter() + : InputEventFilter(InputFilterOrder::WindowSelector) + { + } bool pointerEvent(MouseEvent *event, quint32 nativeButton) override { if (!m_active) { @@ -873,6 +901,7 @@ class GlobalShortcutFilter : public InputEventFilter { public: GlobalShortcutFilter() + : InputEventFilter(InputFilterOrder::GlobalShortcut) { m_powerDown.setSingleShot(true); m_powerDown.setInterval(1000); @@ -1181,6 +1210,11 @@ std::pair performWindowWheelAction(QWheelEvent *event, Window *windo class InternalWindowEventFilter : public InputEventFilter { +public: + InternalWindowEventFilter() + : InputEventFilter(InputFilterOrder::InternalWindow) + { + } bool pointerEvent(MouseEvent *event, quint32 nativeButton) override { if (!input()->pointer()->focus() || !input()->pointer()->focus()->isInternal()) { @@ -1387,6 +1421,10 @@ private: class DecorationEventFilter : public InputEventFilter { public: + DecorationEventFilter() + : InputEventFilter(InputFilterOrder::Decoration) + { + } bool pointerEvent(MouseEvent *event, quint32 nativeButton) override { auto decoration = input()->pointer()->decoration(); @@ -1603,6 +1641,10 @@ private: class TabBoxInputFilter : public InputEventFilter { public: + TabBoxInputFilter() + : InputEventFilter(InputFilterOrder::TabBox) + { + } bool pointerEvent(MouseEvent *event, quint32 button) override { if (!workspace()->tabbox() || !workspace()->tabbox()->isGrabbed()) { @@ -1646,6 +1688,10 @@ public: class ScreenEdgeInputFilter : public InputEventFilter { public: + ScreenEdgeInputFilter() + : InputEventFilter(InputFilterOrder::ScreenEdge) + { + } bool pointerEvent(MouseEvent *event, quint32 nativeButton) override { workspace()->screenEdges()->isEntered(event); @@ -1702,6 +1748,10 @@ private: class WindowActionInputFilter : public InputEventFilter { public: + WindowActionInputFilter() + : InputEventFilter(InputFilterOrder::WindowAction) + { + } bool pointerEvent(MouseEvent *event, quint32 nativeButton) override { if (event->type() != QEvent::MouseButtonPress) { @@ -1771,6 +1821,10 @@ public: class InputKeyboardFilter : public InputEventFilter { public: + InputKeyboardFilter() + : InputEventFilter(InputFilterOrder::InputMethod) + { + } bool keyEvent(KeyEvent *event) override { return passToInputMethod(event); @@ -1783,6 +1837,10 @@ public: class ForwardInputFilter : public InputEventFilter { public: + ForwardInputFilter() + : InputEventFilter(InputFilterOrder::Forward) + { + } bool pointerEvent(MouseEvent *event, quint32 nativeButton) override { auto seat = waylandServer()->seat(); @@ -2008,6 +2066,7 @@ class TabletInputFilter : public QObject, public InputEventFilter { public: TabletInputFilter() + : InputEventFilter(InputFilterOrder::Tablet) { const auto devices = input()->devices(); for (InputDevice *device : devices) { @@ -2386,6 +2445,7 @@ class DragAndDropInputFilter : public QObject, public InputEventFilter Q_OBJECT public: DragAndDropInputFilter() + : InputEventFilter(InputFilterOrder::DragAndDrop) { connect(waylandServer()->seat(), &SeatInterface::dragStarted, this, []() { AbstractDataSource *dragSource = waylandServer()->seat()->dragSource(); @@ -2693,13 +2753,11 @@ InputRedirection::~InputRedirection() void InputRedirection::installInputEventFilter(InputEventFilter *filter) { Q_ASSERT(!m_filters.contains(filter)); - m_filters << filter; -} -void InputRedirection::prependInputEventFilter(InputEventFilter *filter) -{ - Q_ASSERT(!m_filters.contains(filter)); - m_filters.prepend(filter); + auto it = std::lower_bound(m_filters.begin(), m_filters.end(), filter, [](InputEventFilter *a, InputEventFilter *b) { + return a->weight() < b->weight(); + }); + m_filters.insert(it, filter); } void InputRedirection::uninstallInputEventFilter(InputEventFilter *filter) diff --git a/src/input.h b/src/input.h index 9494e3194c..65dcbb7256 100644 --- a/src/input.h +++ b/src/input.h @@ -136,14 +136,7 @@ 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 prependInputEventFilter(InputEventFilter *filter); + void installInputEventFilter(InputEventFilter *filter); void uninstallInputEventFilter(InputEventFilter *filter); /** @@ -311,7 +304,6 @@ private: void setupTouchpadShortcuts(); void setupWorkspace(); void setupInputFilters(); - void installInputEventFilter(InputEventFilter *filter); void updateLeds(LEDs leds); void updateAvailableInputDevices(); KeyboardInputRedirection *m_keyboard; @@ -367,6 +359,35 @@ private: friend class ForwardInputFilter; }; +namespace InputFilterOrder +{ +enum Order { + PlaceholderOutput, + Dpms, + ButtonRebind, + BounceKeys, + StickyKeys, + EisInput, + + VirtualTerminal, + LockScreen, + ScreenEdge, + DragAndDrop, + WindowSelector, + TabBox, + GlobalShortcut, + Effects, + InteractiveMoveResize, + Popup, + Decoration, + WindowAction, + InternalWindow, + InputMethod, + Forward, + Tablet +}; +} + /** * Base class for filtering input events inside InputRedirection. * @@ -389,9 +410,22 @@ private: class KWIN_EXPORT InputEventFilter { public: - InputEventFilter(); + /** + * Construct and install the InputEventFilter + * @param weight The position in the input chain, lower values come first. + * @note the filter is not installed automatically + */ + InputEventFilter(InputFilterOrder::Order weight); + /** + * @brief ~InputEventFilter + * This will uninstall the event filter if needed + */ virtual ~InputEventFilter(); + /** + * The position in the input chain, lower values come first. + */ + int weight() const; /** * Event filter for pointer events which can be described by a QMouseEvent. * @@ -451,6 +485,9 @@ public: protected: void passToWaylandServer(QKeyEvent *event); bool passToInputMethod(QKeyEvent *event); + +private: + int m_weight = 0; }; class KWIN_EXPORT InputDeviceHandler : public QObject diff --git a/src/placeholderinputeventfilter.cpp b/src/placeholderinputeventfilter.cpp index 4839b5a7f2..be8c0f95df 100644 --- a/src/placeholderinputeventfilter.cpp +++ b/src/placeholderinputeventfilter.cpp @@ -12,6 +12,11 @@ namespace KWin { +PlaceholderInputEventFilter::PlaceholderInputEventFilter() + : InputEventFilter(InputFilterOrder::PlaceholderOutput) +{ +} + bool PlaceholderInputEventFilter::pointerEvent(MouseEvent *event, quint32 nativeButton) { return true; diff --git a/src/placeholderinputeventfilter.h b/src/placeholderinputeventfilter.h index 2272a4357f..0d6504076d 100644 --- a/src/placeholderinputeventfilter.h +++ b/src/placeholderinputeventfilter.h @@ -16,6 +16,7 @@ namespace KWin class PlaceholderInputEventFilter : public InputEventFilter { public: + PlaceholderInputEventFilter(); bool pointerEvent(MouseEvent *event, quint32 nativeButton) override; bool wheelEvent(WheelEvent *event) override; bool keyEvent(KeyEvent *event) override; diff --git a/src/plugins/bouncekeys/bouncekeys.cpp b/src/plugins/bouncekeys/bouncekeys.cpp index 27bf015cc9..0b74c56bf6 100644 --- a/src/plugins/bouncekeys/bouncekeys.cpp +++ b/src/plugins/bouncekeys/bouncekeys.cpp @@ -8,7 +8,8 @@ #include "keyboard_input.h" BounceKeysFilter::BounceKeysFilter() - : m_configWatcher(KConfigWatcher::create(KSharedConfig::openConfig("kaccessrc"))) + : KWin::InputEventFilter(KWin::InputFilterOrder::BounceKeys) + , m_configWatcher(KConfigWatcher::create(KSharedConfig::openConfig("kaccessrc"))) { const QLatin1String groupName("Keyboard"); connect(m_configWatcher.get(), &KConfigWatcher::configChanged, this, [this, groupName](const KConfigGroup &group) { @@ -24,7 +25,7 @@ void BounceKeysFilter::loadConfig(const KConfigGroup &group) KWin::input()->uninstallInputEventFilter(this); if (group.readEntry("BounceKeys", false)) { - KWin::input()->prependInputEventFilter(this); + KWin::input()->installInputEventFilter(this); m_delay = std::chrono::milliseconds(group.readEntry("BounceKeysDelay", 500)); } else { diff --git a/src/plugins/buttonrebinds/buttonrebindsfilter.cpp b/src/plugins/buttonrebinds/buttonrebindsfilter.cpp index 8e12694e2e..a0d36e6881 100644 --- a/src/plugins/buttonrebinds/buttonrebindsfilter.cpp +++ b/src/plugins/buttonrebinds/buttonrebindsfilter.cpp @@ -120,7 +120,8 @@ bool InputDevice::isTouchpad() const } ButtonRebindsFilter::ButtonRebindsFilter() - : m_configWatcher(KConfigWatcher::create(KSharedConfig::openConfig("kcminputrc"))) + : KWin::InputEventFilter(KWin::InputFilterOrder::ButtonRebind) + , m_configWatcher(KConfigWatcher::create(KSharedConfig::openConfig("kcminputrc"))) { KWin::input()->addInputDevice(&m_inputDevice); const QLatin1String groupName("ButtonRebinds"); @@ -189,7 +190,7 @@ void ButtonRebindsFilter::loadConfig(const KConfigGroup &group) } if (foundActions) { - KWin::input()->prependInputEventFilter(this); + KWin::input()->installInputEventFilter(this); } } diff --git a/src/plugins/eis/eisinputcapturefilter.cpp b/src/plugins/eis/eisinputcapturefilter.cpp index 6c76797579..6f30abda67 100644 --- a/src/plugins/eis/eisinputcapturefilter.cpp +++ b/src/plugins/eis/eisinputcapturefilter.cpp @@ -16,7 +16,8 @@ namespace KWin { EisInputCaptureFilter::EisInputCaptureFilter(EisInputCaptureManager *manager) - : m_manager(manager) + : InputEventFilter(InputFilterOrder::EisInput) + , m_manager(manager) { } diff --git a/src/plugins/eis/eisinputcapturemanager.cpp b/src/plugins/eis/eisinputcapturemanager.cpp index 20cca4144a..0056665881 100644 --- a/src/plugins/eis/eisinputcapturemanager.cpp +++ b/src/plugins/eis/eisinputcapturemanager.cpp @@ -174,7 +174,7 @@ void EisInputCaptureManager::barrierHit(KWin::EisInputCapture *capture, const QP } m_activeCapture = capture; capture->activate(position); - input()->prependInputEventFilter(m_inputFilter.get()); + input()->installInputEventFilter(m_inputFilter.get()); // Even though the input events are filtered out the cursor is updated on screen which looks weird Cursors::self()->hideCursor(); } diff --git a/src/plugins/stickykeys/stickykeys.cpp b/src/plugins/stickykeys/stickykeys.cpp index 887fb0ff45..54bcf8ab4a 100644 --- a/src/plugins/stickykeys/stickykeys.cpp +++ b/src/plugins/stickykeys/stickykeys.cpp @@ -29,7 +29,8 @@ static const std::array modifiers = { }; StickyKeysFilter::StickyKeysFilter() - : m_configWatcher(KConfigWatcher::create(KSharedConfig::openConfig("kaccessrc"))) + : KWin::InputEventFilter(KWin::InputFilterOrder::StickyKeys) + , m_configWatcher(KConfigWatcher::create(KSharedConfig::openConfig("kaccessrc"))) { const QLatin1String groupName("Keyboard"); connect(m_configWatcher.get(), &KConfigWatcher::configChanged, this, [this, groupName](const KConfigGroup &group) { @@ -82,7 +83,7 @@ void StickyKeysFilter::loadConfig(const KConfigGroup &group) } if (group.readEntry("StickyKeys", false)) { - KWin::input()->prependInputEventFilter(this); + KWin::input()->installInputEventFilter(this); } else { // sticky keys are deactivated, unlatch all latched/locked keys for (auto it = m_keyStates.keyValueBegin(); it != m_keyStates.keyValueEnd(); ++it) { diff --git a/src/popup_input_filter.cpp b/src/popup_input_filter.cpp index 751b1721a8..5b288b61bb 100644 --- a/src/popup_input_filter.cpp +++ b/src/popup_input_filter.cpp @@ -18,6 +18,7 @@ namespace KWin PopupInputFilter::PopupInputFilter() : QObject() + , InputEventFilter(InputFilterOrder::Popup) { connect(workspace(), &Workspace::windowAdded, this, &PopupInputFilter::handleWindowAdded); } diff --git a/src/workspace.cpp b/src/workspace.cpp index 7edf6faeaa..02d6b48194 100644 --- a/src/workspace.cpp +++ b/src/workspace.cpp @@ -1235,7 +1235,7 @@ void Workspace::updateOutputs(const std::optional> &outputOrder) if (!m_placeholderOutput) { m_placeholderOutput = new PlaceholderOutput(QSize(1920, 1080), 1); m_placeholderFilter = std::make_unique(); - input()->prependInputEventFilter(m_placeholderFilter.get()); + input()->installInputEventFilter(m_placeholderFilter.get()); } m_outputs.append(m_placeholderOutput); } else { @@ -1345,7 +1345,7 @@ void Workspace::createDpmsFilter() { if (!m_dpmsFilter) { m_dpmsFilter = std::make_unique(); - input()->prependInputEventFilter(m_dpmsFilter.get()); + input()->installInputEventFilter(m_dpmsFilter.get()); } }