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.
This commit is contained in:
David Edmundson 2024-08-01 08:46:06 +00:00
parent f22ef1f090
commit 99caa54901
12 changed files with 134 additions and 28 deletions

View file

@ -23,7 +23,7 @@ namespace KWin
{
DpmsInputEventFilter::DpmsInputEventFilter()
: InputEventFilter()
: InputEventFilter(InputFilterOrder::Dpms)
{
KSharedConfig::Ptr kwinSettings = kwinApp()->config();
m_enableDoubleTap = kwinSettings->group(QStringLiteral("Wayland")).readEntry<bool>("DoubleTapWakeup", true);

View file

@ -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<bool, bool> 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)

View file

@ -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

View file

@ -12,6 +12,11 @@
namespace KWin
{
PlaceholderInputEventFilter::PlaceholderInputEventFilter()
: InputEventFilter(InputFilterOrder::PlaceholderOutput)
{
}
bool PlaceholderInputEventFilter::pointerEvent(MouseEvent *event, quint32 nativeButton)
{
return true;

View file

@ -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;

View file

@ -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<bool>("BounceKeys", false)) {
KWin::input()->prependInputEventFilter(this);
KWin::input()->installInputEventFilter(this);
m_delay = std::chrono::milliseconds(group.readEntry<int>("BounceKeysDelay", 500));
} else {

View file

@ -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);
}
}

View file

@ -16,7 +16,8 @@
namespace KWin
{
EisInputCaptureFilter::EisInputCaptureFilter(EisInputCaptureManager *manager)
: m_manager(manager)
: InputEventFilter(InputFilterOrder::EisInput)
, m_manager(manager)
{
}

View file

@ -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();
}

View file

@ -29,7 +29,8 @@ static const std::array<Modifier, 5> 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<bool>("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) {

View file

@ -18,6 +18,7 @@ namespace KWin
PopupInputFilter::PopupInputFilter()
: QObject()
, InputEventFilter(InputFilterOrder::Popup)
{
connect(workspace(), &Workspace::windowAdded, this, &PopupInputFilter::handleWindowAdded);
}

View file

@ -1235,7 +1235,7 @@ void Workspace::updateOutputs(const std::optional<QList<Output *>> &outputOrder)
if (!m_placeholderOutput) {
m_placeholderOutput = new PlaceholderOutput(QSize(1920, 1080), 1);
m_placeholderFilter = std::make_unique<PlaceholderInputEventFilter>();
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<DpmsInputEventFilter>();
input()->prependInputEventFilter(m_dpmsFilter.get());
input()->installInputEventFilter(m_dpmsFilter.get());
}
}