xwayland: Add option to additionally eavesdrop on mouse buttons

BUG: 466448
This commit is contained in:
Oliver Beard 2023-11-02 19:15:40 +00:00
parent d13b6db706
commit 183637502d
8 changed files with 103 additions and 28 deletions

View file

@ -14,5 +14,8 @@
</choices>
<default>None</default>
</entry>
<entry name="XwaylandEavesdropsMouse" type="Bool">
<default>false</default>
</entry>
</group>
</kcfg>

View file

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

View file

@ -339,5 +339,8 @@
</choices>
<default>None</default>
</entry>
<entry name="XwaylandEavesdropsMouse" type="Bool">
<default>false</default>
</entry>
</group>
</kcfg>

View file

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

View file

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

View file

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

View file

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

View file

@ -33,6 +33,7 @@
#include <KSelectionOwner>
#include <wayland/keyboard.h>
#include <wayland/pointer.h>
#include <wayland/seat.h>
#include <wayland/surface.h>
@ -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<quint32, KeyboardKeyState> m_states;
std::function<bool(int key, Qt::KeyboardModifiers)> m_filter;
std::function<bool(int key, Qt::KeyboardModifiers)> 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<XwaylandInputSpy>();
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();