From 183637502d6f081f470a6ea1f99b69f882af68bb Mon Sep 17 00:00:00 2001 From: Oliver Beard Date: Thu, 2 Nov 2023 19:15:40 +0000 Subject: [PATCH] xwayland: Add option to additionally eavesdrop on mouse buttons BUG: 466448 --- src/kcms/xwayland/kwinxwaylandsettings.kcfg | 3 ++ src/kcms/xwayland/ui/main.qml | 43 ++++++++++-------- src/kwin.kcfg | 3 ++ src/options.cpp | 11 +++++ src/options.h | 11 +++++ src/wayland/pointer.cpp | 9 ++++ src/wayland/pointer.h | 2 + src/xwayland/xwayland.cpp | 49 ++++++++++++++++----- 8 files changed, 103 insertions(+), 28 deletions(-) diff --git a/src/kcms/xwayland/kwinxwaylandsettings.kcfg b/src/kcms/xwayland/kwinxwaylandsettings.kcfg index 5e0c9178fd..b0c78dd729 100644 --- a/src/kcms/xwayland/kwinxwaylandsettings.kcfg +++ b/src/kcms/xwayland/kwinxwaylandsettings.kcfg @@ -14,5 +14,8 @@ None + + false + diff --git a/src/kcms/xwayland/ui/main.qml b/src/kcms/xwayland/ui/main.qml index 34d0422d8a..227347e213 100644 --- a/src/kcms/xwayland/ui/main.qml +++ b/src/kcms/xwayland/ui/main.qml @@ -22,21 +22,6 @@ KCM.SimpleKCM { implicitWidth: Kirigami.Units.gridUnit * 48 implicitHeight: Kirigami.Units.gridUnit * 33 - QQC2.ButtonGroup { - buttons: form.children - exclusive: true - checkedButton: buttons[kcm.settings.xwaylandEavesdrops] - onCheckedButtonChanged: { - let idx = -1 - for (const x in buttons) { - if (buttons[x] === checkedButton) { - idx = x - } - } - kcm.settings.xwaylandEavesdrops = idx - } - } - ColumnLayout { id: column spacing: Kirigami.Units.smallSpacing @@ -55,29 +40,51 @@ KCM.SimpleKCM { } Kirigami.FormLayout { - id: form - Layout.leftMargin: Kirigami.Units.gridUnit Layout.rightMargin: Kirigami.Units.gridUnit QQC2.RadioButton { + id: never Kirigami.FormData.label: i18n("Allow legacy X11 apps to read keystrokes typed in all apps:") text: i18n("Never") + checked: kcm.settings.xwaylandEavesdrops === 0 + onToggled: if (checked) kcm.settings.xwaylandEavesdrops = 0 } + QQC2.RadioButton { - text: i18n("Only non-character keys") + text: i18n("Only Meta, Control, Alt and Shift keys") + checked: kcm.settings.xwaylandEavesdrops === 1 + onToggled: if (checked) kcm.settings.xwaylandEavesdrops = 1 } + QQC2.RadioButton { text: i18n("As above, plus any key typed while the Control, Alt, or Meta keys are pressed") + checked: kcm.settings.xwaylandEavesdrops === 2 + onToggled: if (checked) kcm.settings.xwaylandEavesdrops = 2 } + QQC2.RadioButton { id: always text: i18n("Always") + checked: kcm.settings.xwaylandEavesdrops === 3 + onToggled: if (checked) kcm.settings.xwaylandEavesdrops = 3 + } + + Item { + Kirigami.FormData.isSection: true + } + + QQC2.CheckBox { + text: i18n("Additionally include mouse buttons") + checked: kcm.settings.xwaylandEavesdropsMouse + onToggled: kcm.settings.xwaylandEavesdropsMouse = checked + enabled: !never.checked } } Kirigami.InlineMessage { Layout.fillWidth: true + Layout.margins: Kirigami.Units.gridUnit type: Kirigami.MessageType.Warning text: i18n("Note that using this setting will reduce system security to that of the X11 session by permitting malicious software to steal passwords and spy on the text that you type. Make sure you understand and accept this risk.") visible: always.checked diff --git a/src/kwin.kcfg b/src/kwin.kcfg index b8da5e9896..7bb834506a 100644 --- a/src/kwin.kcfg +++ b/src/kwin.kcfg @@ -339,5 +339,8 @@ None + + false + diff --git a/src/options.cpp b/src/options.cpp index b644e0f1a2..44ed8d87e6 100644 --- a/src/options.cpp +++ b/src/options.cpp @@ -55,6 +55,7 @@ Options::Options(QObject *parent) , m_xwaylandCrashPolicy(Options::defaultXwaylandCrashPolicy()) , m_xwaylandMaxCrashCount(Options::defaultXwaylandMaxCrashCount()) , m_xwaylandEavesdrops(Options::defaultXwaylandEavesdrops()) + , m_xwaylandEavesdropsMouse(Options::defaultXwaylandEavesdropsMouse()) , m_compositingMode(Options::defaultCompositingMode()) , m_useCompositing(Options::defaultUseCompositing()) , m_hiddenPreviews(Options::defaultHiddenPreviews()) @@ -156,6 +157,15 @@ void Options::setXwaylandEavesdrops(XwaylandEavesdropsMode mode) Q_EMIT xwaylandEavesdropsChanged(); } +void Options::setXwaylandEavesdropsMouse(bool eavesdropsMouse) +{ + if (m_xwaylandEavesdropsMouse == eavesdropsMouse) { + return; + } + m_xwaylandEavesdropsMouse = eavesdropsMouse; + Q_EMIT xwaylandEavesdropsChanged(); +} + void Options::setClickRaise(bool clickRaise) { if (m_autoRaise) { @@ -827,6 +837,7 @@ void Options::syncFromKcfgc() setXwaylandCrashPolicy(m_settings->xwaylandCrashPolicy()); setXwaylandMaxCrashCount(m_settings->xwaylandMaxCrashCount()); setXwaylandEavesdrops(XwaylandEavesdropsMode(m_settings->xwaylandEavesdrops())); + setXwaylandEavesdropsMouse(m_settings->xwaylandEavesdropsMouse()); setPlacement(m_settings->placement()); setAutoRaise(m_settings->autoRaise()); setAutoRaiseInterval(m_settings->autoRaiseInterval()); diff --git a/src/options.h b/src/options.h index 63e39b947d..8437eceb25 100644 --- a/src/options.h +++ b/src/options.h @@ -262,6 +262,10 @@ public: { return m_xwaylandEavesdrops; } + bool xwaylandEavesdropsMouse() const + { + return m_xwaylandEavesdropsMouse; + } /** * Whether clicking on a window raises it in FocusFollowsMouse @@ -692,6 +696,7 @@ public: void setXwaylandCrashPolicy(XwaylandCrashPolicy crashPolicy); void setXwaylandMaxCrashCount(int maxCrashCount); void setXwaylandEavesdrops(XwaylandEavesdropsMode mode); + void setXwaylandEavesdropsMouse(bool eavesdropsMouse); void setNextFocusPrefersMouse(bool nextFocusPrefersMouse); void setClickRaise(bool clickRaise); void setAutoRaise(bool autoRaise); @@ -870,6 +875,10 @@ public: { return None; } + static bool defaultXwaylandEavesdropsMouse() + { + return false; + } static ActivationDesktopPolicy defaultActivationDesktopPolicy() { return ActivationDesktopPolicy::SwitchToOtherDesktop; @@ -888,6 +897,7 @@ Q_SIGNALS: void xwaylandCrashPolicyChanged(); void xwaylandMaxCrashCountChanged(); void xwaylandEavesdropsChanged(); + void xwaylandEavesdropsMouseChanged(); void nextFocusPrefersMouseChanged(); void clickRaiseChanged(); void autoRaiseChanged(); @@ -972,6 +982,7 @@ private: XwaylandCrashPolicy m_xwaylandCrashPolicy; int m_xwaylandMaxCrashCount; XwaylandEavesdropsMode m_xwaylandEavesdrops; + bool m_xwaylandEavesdropsMouse; CompositingType m_compositingMode; bool m_useCompositing; diff --git a/src/wayland/pointer.cpp b/src/wayland/pointer.cpp index 3cead34ec9..6c826bf0a3 100644 --- a/src/wayland/pointer.cpp +++ b/src/wayland/pointer.cpp @@ -222,6 +222,15 @@ void PointerInterface::sendButton(quint32 button, PointerButtonState state, quin } } +void PointerInterface::sendButton(quint32 button, PointerButtonState state, ClientConnection *client) +{ + const auto pointerResources = d->pointersForClient(client); + const quint32 serial = d->seat->display()->nextSerial(); + for (PointerInterfacePrivate::Resource *resource : pointerResources) { + d->send_button(resource->handle, serial, d->seat->timestamp().count(), button, quint32(state)); + } +} + static void updateAccumulators(Qt::Orientation orientation, qreal delta, qint32 deltaV120, PointerInterfacePrivate *d, qint32 &valueAxisLowRes, qint32 &valueDiscrete) { const int newDirection = deltaV120 > 0 ? 1 : -1; diff --git a/src/wayland/pointer.h b/src/wayland/pointer.h index ec51fb044b..da28044205 100644 --- a/src/wayland/pointer.h +++ b/src/wayland/pointer.h @@ -19,6 +19,7 @@ namespace KWin class PointerSurfaceCursorPrivate; class PointerSurfaceCursor; class PointerInterfacePrivate; +class ClientConnection; class SeatInterface; class SurfaceInterface; @@ -60,6 +61,7 @@ public: void sendEnter(SurfaceInterface *surface, const QPointF &position, quint32 serial); void sendLeave(quint32 serial); void sendButton(quint32 button, PointerButtonState state, quint32 serial); + void sendButton(quint32 button, PointerButtonState state, ClientConnection *client); void sendAxis(Qt::Orientation orientation, qreal delta, qint32 deltaV120, PointerAxisSource source, PointerAxisRelativeDirection direction); void sendMotion(const QPointF &position); void sendFrame(); diff --git a/src/xwayland/xwayland.cpp b/src/xwayland/xwayland.cpp index 10bf086d8a..7a6959e9f6 100644 --- a/src/xwayland/xwayland.cpp +++ b/src/xwayland/xwayland.cpp @@ -33,6 +33,7 @@ #include #include +#include #include #include @@ -108,7 +109,7 @@ public: }); } - void setMode(XwaylandEavesdropsMode mode) + void setMode(XwaylandEavesdropsMode mode, bool eavesdropsMouse) { static const Qt::KeyboardModifiers modifierKeys = { Qt::ControlModifier, @@ -337,22 +338,26 @@ public: switch (mode) { case None: - m_filter = {}; + m_filterKey = {}; + m_filterMouse = false; break; case NonCharacterKeys: - m_filter = [](int key, Qt::KeyboardModifiers) { + m_filterKey = [](int key, Qt::KeyboardModifiers) { return !characterKeys.contains(key); }; + m_filterMouse = eavesdropsMouse; break; case AllKeysWithModifier: - m_filter = [](int key, Qt::KeyboardModifiers m) { + m_filterKey = [](int key, Qt::KeyboardModifiers m) { return m.testAnyFlags(modifierKeys) || !characterKeys.contains(key); }; + m_filterMouse = eavesdropsMouse; break; case All: - m_filter = [](int, Qt::KeyboardModifiers) { + m_filterKey = [](int, Qt::KeyboardModifiers) { return true; }; + m_filterMouse = eavesdropsMouse; break; } } @@ -364,7 +369,7 @@ public: } Window *window = workspace()->activeWindow(); - if (!m_filter || !m_filter(event->key(), event->modifiers()) || (window && window->isLockScreen())) { + if (!m_filterKey || !m_filterKey(event->key(), event->modifiers()) || (window && window->isLockScreen())) { return; } @@ -388,7 +393,29 @@ public: xkb->modifierState().locked, xkb->currentLayout()); - waylandServer()->seat()->keyboard()->sendKey(event->nativeScanCode(), state, xwaylandClient); + keyboard->sendKey(event->nativeScanCode(), state, xwaylandClient); + } + } + + void pointerEvent(KWin::MouseEvent *event) override + { + Window *window = workspace()->activeWindow(); + if (!m_filterMouse || (window && window->isLockScreen())) { + return; + } + + auto pointer = waylandServer()->seat()->pointer(); + auto surface = pointer->focusedSurface(); + if (!surface) { + return; + } + + ClientConnection *client = surface->client(); + ClientConnection *xwaylandClient = waylandServer()->xWaylandConnection(); + if (xwaylandClient && xwaylandClient != client) { + PointerButtonState state{event->type() == QEvent::MouseButtonPress}; + + pointer->sendButton(event->nativeButton(), state, xwaylandClient); } } @@ -407,7 +434,8 @@ public: } QHash m_states; - std::function m_filter; + std::function m_filterKey; + bool m_filterMouse = false; }; Xwayland::Xwayland(Application *app) @@ -547,6 +575,7 @@ void Xwayland::handleXwaylandReady() refreshEavesdropping(); connect(options, &Options::xwaylandEavesdropsChanged, this, &Xwayland::refreshEavesdropping); + connect(options, &Options::xwaylandEavesdropsMouseChanged, this, &Xwayland::refreshEavesdropping); Q_EMIT started(); } @@ -560,7 +589,7 @@ void Xwayland::refreshEavesdropping() const bool enabled = options->xwaylandEavesdrops() != None; if (enabled == bool(m_inputSpy)) { if (m_inputSpy) { - m_inputSpy->setMode(options->xwaylandEavesdrops()); + m_inputSpy->setMode(options->xwaylandEavesdrops(), options->xwaylandEavesdropsMouse()); } return; } @@ -568,7 +597,7 @@ void Xwayland::refreshEavesdropping() if (enabled) { m_inputSpy = std::make_unique(); input()->installInputEventSpy(m_inputSpy.get()); - m_inputSpy->setMode(options->xwaylandEavesdrops()); + m_inputSpy->setMode(options->xwaylandEavesdrops(), options->xwaylandEavesdropsMouse()); } else { input()->uninstallInputEventSpy(m_inputSpy.get()); m_inputSpy.reset();