Xwayland: Allow users to optionally let Xwayland eavesdrop on certain modes
It's somewhat popular for voice communication applications to support Push-to-Talk. This means that the process itself expects to get all of the system input. This behaviour albeit sound does not work on Wayland systems. This commit adds an option to let legacy X11 applications that assume they will be getting all information to do so until these apps are properly ported to the XDP GlobalShortcuts.
This commit is contained in:
parent
245859637f
commit
fc2447071e
21 changed files with 429 additions and 8 deletions
|
@ -238,12 +238,13 @@ void InputEventFilter::passToWaylandServer(QKeyEvent *event)
|
|||
}
|
||||
|
||||
KWaylandServer::SeatInterface *seat = waylandServer()->seat();
|
||||
const int keyCode = event->nativeScanCode();
|
||||
switch (event->type()) {
|
||||
case QEvent::KeyPress:
|
||||
seat->notifyKeyboardKey(event->nativeScanCode(), KWaylandServer::KeyboardKeyState::Pressed);
|
||||
seat->notifyKeyboardKey(keyCode, KWaylandServer::KeyboardKeyState::Pressed);
|
||||
break;
|
||||
case QEvent::KeyRelease:
|
||||
seat->notifyKeyboardKey(event->nativeScanCode(), KWaylandServer::KeyboardKeyState::Released);
|
||||
seat->notifyKeyboardKey(keyCode, KWaylandServer::KeyboardKeyState::Released);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
|
@ -615,7 +615,8 @@ void InputMethod::setPreeditString(uint32_t serial, const QString &text, const Q
|
|||
|
||||
void InputMethod::key(quint32 /*serial*/, quint32 /*time*/, quint32 keyCode, bool pressed)
|
||||
{
|
||||
waylandServer()->seat()->notifyKeyboardKey(keyCode, pressed ? KWaylandServer::KeyboardKeyState::Pressed : KWaylandServer::KeyboardKeyState::Released);
|
||||
waylandServer()->seat()->notifyKeyboardKey(keyCode,
|
||||
pressed ? KWaylandServer::KeyboardKeyState::Pressed : KWaylandServer::KeyboardKeyState::Released);
|
||||
}
|
||||
|
||||
void InputMethod::modifiers(quint32 serial, quint32 mods_depressed, quint32 mods_latched, quint32 mods_locked, quint32 group)
|
||||
|
|
|
@ -10,6 +10,7 @@ add_subdirectory(kwinscripts)
|
|||
add_subdirectory(kwindesktop)
|
||||
add_subdirectory(kwineffects)
|
||||
add_subdirectory(kwinvirtualkeyboard)
|
||||
add_subdirectory(kwinxwayland)
|
||||
|
||||
if (KWIN_BUILD_TABBOX)
|
||||
add_subdirectory(kwintabbox)
|
||||
|
|
25
src/kcmkwin/kwinxwayland/CMakeLists.txt
Normal file
25
src/kcmkwin/kwinxwayland/CMakeLists.txt
Normal file
|
@ -0,0 +1,25 @@
|
|||
#SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalzez <aleixpol@kde.org>
|
||||
#SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
add_definitions(-DTRANSLATION_DOMAIN=\"kcm_kwinxwayland\")
|
||||
|
||||
kcmutils_generate_module_data(
|
||||
kcm_kwinxwayland_PART_SRCS
|
||||
MODULE_DATA_HEADER kwinxwaylanddata.h
|
||||
MODULE_DATA_CLASS_NAME KWinXwaylandData
|
||||
SETTINGS_HEADERS kwinxwaylandsettings.h
|
||||
SETTINGS_CLASSES KWinXwaylandSettings
|
||||
)
|
||||
|
||||
kconfig_add_kcfg_files(kcm_kwinxwayland_PART_SRCS kwinxwaylandsettings.kcfgc GENERATE_MOC)
|
||||
kcoreaddons_add_plugin(kcm_kwinxwayland SOURCES kcmkwinxwayland.cpp ${kcm_kwinxwayland_PART_SRCS} INSTALL_NAMESPACE plasma/kcms/systemsettings)
|
||||
target_include_directories(kcm_kwinxwayland PRIVATE ${CMAKE_SOURCE_DIR})
|
||||
kcmutils_generate_desktop_file(kcm_kwinxwayland)
|
||||
|
||||
target_link_libraries(kcm_kwinxwayland
|
||||
KF5::I18n
|
||||
KF5::KCMUtils
|
||||
KF5::QuickAddons
|
||||
Wayland::Client
|
||||
)
|
||||
kpackage_install_package(package kcm_kwinxwayland kcms)
|
2
src/kcmkwin/kwinxwayland/Messages.sh
Normal file
2
src/kcmkwin/kwinxwayland/Messages.sh
Normal file
|
@ -0,0 +1,2 @@
|
|||
#! /usr/bin/env bash
|
||||
$XGETTEXT `find . -name \*.cpp -o -name \*.h -o -name \*.qml` -o $podir/kcm_kwinxwayland.pot
|
15
src/kcmkwin/kwinxwayland/kcm_kwinxwayland.json
Normal file
15
src/kcmkwin/kwinxwayland/kcm_kwinxwayland.json
Normal file
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"Categories": "Qt;KDE;",
|
||||
"KPlugin": {
|
||||
"BugReportUrl": "https://bugs.kde.org/enter_bug.cgi?product=systemsettings&component=kcm_kwinxwayland",
|
||||
"Description": "Select which keys will be globally available to legacy X11 apps",
|
||||
"Icon": "xorg",
|
||||
"Name": "Legacy X11 App Support"
|
||||
},
|
||||
"X-KDE-Keywords": "xwayland,global,keys,forward",
|
||||
"X-KDE-OnlyShowOnQtPlatforms": [
|
||||
"wayland"
|
||||
],
|
||||
"X-KDE-ParentApp": "kcontrol",
|
||||
"X-KDE-System-Settings-Parent-Category": "applications"
|
||||
}
|
38
src/kcmkwin/kwinxwayland/kcmkwinxwayland.cpp
Normal file
38
src/kcmkwin/kwinxwayland/kcmkwinxwayland.cpp
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kcmkwinxwayland.h"
|
||||
|
||||
#include <KAboutData>
|
||||
#include <KApplicationTrader>
|
||||
#include <KConfigGroup>
|
||||
#include <KDesktopFile>
|
||||
#include <KLocalizedString>
|
||||
#include <KPluginFactory>
|
||||
#include <QKeySequence>
|
||||
|
||||
#include <kwinxwaylanddata.h>
|
||||
|
||||
K_PLUGIN_FACTORY_WITH_JSON(KcmXwaylandFactory, "kcm_kwinxwayland.json", registerPlugin<KcmXwayland>(); registerPlugin<KWinXwaylandData>();)
|
||||
|
||||
KcmXwayland::KcmXwayland(QObject *parent, const QVariantList &args)
|
||||
: KQuickAddons::ManagedConfigModule(parent)
|
||||
, m_data(new KWinXwaylandData(this))
|
||||
, m_settings(new KWinXwaylandSettings(m_data))
|
||||
{
|
||||
registerSettings(m_settings);
|
||||
qmlRegisterAnonymousType<KWinXwaylandSettings>("org.kde.kwin.kwinxwaylandsettings", 1);
|
||||
|
||||
setAboutData(new KAboutData(QStringLiteral("kcm_kwinxwayland"),
|
||||
i18n("Legacy X11 App Support"),
|
||||
QStringLiteral("1.0"),
|
||||
i18n("Allow legacy X11 apps to read keystrokes typed in other apps"),
|
||||
KAboutLicense::GPL));
|
||||
}
|
||||
|
||||
KcmXwayland::~KcmXwayland() = default;
|
||||
|
||||
#include "kcmkwinxwayland.moc"
|
36
src/kcmkwin/kwinxwayland/kcmkwinxwayland.h
Normal file
36
src/kcmkwin/kwinxwayland/kcmkwinxwayland.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <KQuickAddons/ManagedConfigModule>
|
||||
#include <KService>
|
||||
#include <QAbstractListModel>
|
||||
|
||||
#include <kwinxwaylandsettings.h>
|
||||
|
||||
class KWinXwaylandData;
|
||||
|
||||
class KcmXwayland : public KQuickAddons::ManagedConfigModule
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(KWinXwaylandSettings *settings READ settings CONSTANT)
|
||||
|
||||
public:
|
||||
explicit KcmXwayland(QObject *parent = nullptr, const QVariantList &list = QVariantList());
|
||||
~KcmXwayland() override;
|
||||
|
||||
KWinXwaylandSettings *settings() const
|
||||
{
|
||||
return m_settings;
|
||||
}
|
||||
|
||||
private:
|
||||
void refresh();
|
||||
|
||||
KWinXwaylandData *const m_data;
|
||||
KWinXwaylandSettings *const m_settings;
|
||||
};
|
18
src/kcmkwin/kwinxwayland/kwinxwaylandsettings.kcfg
Normal file
18
src/kcmkwin/kwinxwayland/kwinxwaylandsettings.kcfg
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
|
||||
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
|
||||
<kcfgfile name="kwinrc"/>
|
||||
<group name="Xwayland">
|
||||
<entry name="XwaylandEavesdrops" type="Enum">
|
||||
<choices>
|
||||
<choice name="None"/>
|
||||
<choice name="Modifiers"/>
|
||||
<choice name="Combinations"/>
|
||||
<choice name="All"/>
|
||||
</choices>
|
||||
<default>None</default>
|
||||
</entry>
|
||||
</group>
|
||||
</kcfg>
|
8
src/kcmkwin/kwinxwayland/kwinxwaylandsettings.kcfgc
Normal file
8
src/kcmkwin/kwinxwayland/kwinxwaylandsettings.kcfgc
Normal file
|
@ -0,0 +1,8 @@
|
|||
File=kwinxwaylandsettings.kcfg
|
||||
ClassName=KWinXwaylandSettings
|
||||
Mutators=true
|
||||
DefaultValueGetters=true
|
||||
GenerateProperties=true
|
||||
ParentInConstructor=true
|
||||
Notifiers=true
|
||||
GlobalEnums=true
|
70
src/kcmkwin/kwinxwayland/package/contents/ui/main.qml
Normal file
70
src/kcmkwin/kwinxwayland/package/contents/ui/main.qml
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
SPDX-FileCopyrightText: 2020 Aleix Pol Gonzalez <aleixpol@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
|
||||
|
||||
import QtQuick 2.1
|
||||
import QtQuick.Layouts 1.1
|
||||
import QtQuick.Controls 2.3 as QQC2
|
||||
import org.kde.kirigami 2.6 as Kirigami
|
||||
import org.kde.kcm 1.3 as KCM
|
||||
import org.kde.kwin.kwinxwaylandsettings 1.0
|
||||
import org.kde.kquickcontrols 2.0
|
||||
|
||||
KCM.SimpleKCM {
|
||||
id: root
|
||||
KCM.ConfigModule.buttons: KCM.ConfigModule.Default | KCM.ConfigModule.Apply
|
||||
KCM.ConfigModule.quickHelp: i18n("This module lets configure which keyboard events are forwarded to X11 apps regardless of their focus.")
|
||||
KCM.SettingStateBinding {
|
||||
configObject: kcm.settings
|
||||
settingName: "Xwayland"
|
||||
}
|
||||
implicitWidth: Kirigami.Units.gridUnit * 48
|
||||
implicitHeight: Kirigami.Units.gridUnit * 33
|
||||
|
||||
QQC2.ButtonGroup {
|
||||
buttons: column.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
|
||||
QQC2.Label {
|
||||
Layout.fillWidth: true
|
||||
text: i18n("Allow legacy X11 apps to read keystrokes typed in all apps:")
|
||||
}
|
||||
|
||||
QQC2.RadioButton {
|
||||
text: i18n("Never")
|
||||
}
|
||||
QQC2.RadioButton {
|
||||
text: i18n("Only Meta, Control, Alt, and Shift keys")
|
||||
}
|
||||
QQC2.RadioButton {
|
||||
text: i18n("All keys, but only while Meta, Ctrl, Alt, or Shift keys are pressed")
|
||||
}
|
||||
QQC2.RadioButton {
|
||||
id: always
|
||||
text: i18n("Always")
|
||||
}
|
||||
|
||||
Kirigami.InlineMessage {
|
||||
Layout.fillWidth: true
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -352,5 +352,15 @@
|
|||
<entry name="XwaylandMaxCrashCount" type="UInt">
|
||||
<default>3</default>
|
||||
</entry>
|
||||
|
||||
<entry name="XwaylandEavesdrops" type="Enum">
|
||||
<choices>
|
||||
<choice name="None"/>
|
||||
<choice name="Modifiers"/>
|
||||
<choice name="Combinations"/>
|
||||
<choice name="All"/>
|
||||
</choices>
|
||||
<default>None</default>
|
||||
</entry>
|
||||
</group>
|
||||
</kcfg>
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include <QProcess>
|
||||
|
||||
#include "settings.h"
|
||||
#include "workspace.h"
|
||||
#include <QOpenGLContext>
|
||||
#include <kwinglplatform.h>
|
||||
|
||||
|
@ -55,6 +56,7 @@ Options::Options(QObject *parent)
|
|||
, m_hideUtilityWindowsForInactive(false)
|
||||
, m_xwaylandCrashPolicy(Options::defaultXwaylandCrashPolicy())
|
||||
, m_xwaylandMaxCrashCount(Options::defaultXwaylandMaxCrashCount())
|
||||
, m_xwaylandEavesdrops(Options::defaultXwaylandEavesdrops())
|
||||
, m_latencyPolicy(Options::defaultLatencyPolicy())
|
||||
, m_renderTimeEstimator(Options::defaultRenderTimeEstimator())
|
||||
, m_compositingMode(Options::defaultCompositingMode())
|
||||
|
@ -97,6 +99,8 @@ Options::Options(QObject *parent)
|
|||
connect(m_configWatcher.data(), &KConfigWatcher::configChanged, this, [this](const KConfigGroup &group, const QByteArrayList &names) {
|
||||
if (group.name() == QLatin1String("KDE") && names.contains(QByteArrayLiteral("AnimationDurationFactor"))) {
|
||||
Q_EMIT animationSpeedChanged();
|
||||
} else if (group.name() == QLatin1String("Xwayland")) {
|
||||
workspace()->reconfigure();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -146,6 +150,15 @@ void Options::setXwaylandMaxCrashCount(int maxCrashCount)
|
|||
Q_EMIT xwaylandMaxCrashCountChanged();
|
||||
}
|
||||
|
||||
void Options::setXwaylandEavesdrops(XwaylandEavesdropsMode mode)
|
||||
{
|
||||
if (m_xwaylandEavesdrops == mode) {
|
||||
return;
|
||||
}
|
||||
m_xwaylandEavesdrops = mode;
|
||||
Q_EMIT xwaylandEavesdropsChanged();
|
||||
}
|
||||
|
||||
void Options::setClickRaise(bool clickRaise)
|
||||
{
|
||||
if (m_autoRaise) {
|
||||
|
@ -785,6 +798,7 @@ void Options::syncFromKcfgc()
|
|||
setActivationDesktopPolicy(m_settings->activationDesktopPolicy());
|
||||
setXwaylandCrashPolicy(m_settings->xwaylandCrashPolicy());
|
||||
setXwaylandMaxCrashCount(m_settings->xwaylandMaxCrashCount());
|
||||
setXwaylandEavesdrops(XwaylandEavesdropsMode(m_settings->xwaylandEavesdrops()));
|
||||
setPlacement(m_settings->placement());
|
||||
setAutoRaise(m_settings->autoRaise());
|
||||
setAutoRaiseInterval(m_settings->autoRaiseInterval());
|
||||
|
|
|
@ -33,6 +33,13 @@ enum HiddenPreviews {
|
|||
HiddenPreviewsAlways
|
||||
};
|
||||
|
||||
enum XwaylandEavesdropsMode {
|
||||
None,
|
||||
Modifiers,
|
||||
Combinations,
|
||||
All
|
||||
};
|
||||
|
||||
/**
|
||||
* This enum type specifies whether the Xwayland server must be restarted after a crash.
|
||||
*/
|
||||
|
@ -275,6 +282,10 @@ public:
|
|||
{
|
||||
return m_xwaylandMaxCrashCount;
|
||||
}
|
||||
XwaylandEavesdropsMode xwaylandEavesdrops() const
|
||||
{
|
||||
return m_xwaylandEavesdrops;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether clicking on a window raises it in FocusFollowsMouse
|
||||
|
@ -711,6 +722,7 @@ public:
|
|||
void setFocusPolicy(FocusPolicy focusPolicy);
|
||||
void setXwaylandCrashPolicy(XwaylandCrashPolicy crashPolicy);
|
||||
void setXwaylandMaxCrashCount(int maxCrashCount);
|
||||
void setXwaylandEavesdrops(XwaylandEavesdropsMode mode);
|
||||
void setNextFocusPrefersMouse(bool nextFocusPrefersMouse);
|
||||
void setClickRaise(bool clickRaise);
|
||||
void setAutoRaise(bool autoRaise);
|
||||
|
@ -888,6 +900,10 @@ public:
|
|||
{
|
||||
return 3;
|
||||
}
|
||||
static XwaylandEavesdropsMode defaultXwaylandEavesdrops()
|
||||
{
|
||||
return None;
|
||||
}
|
||||
static LatencyPolicy defaultLatencyPolicy()
|
||||
{
|
||||
return LatencyMedium;
|
||||
|
@ -917,6 +933,7 @@ Q_SIGNALS:
|
|||
void focusPolicyIsResonableChanged();
|
||||
void xwaylandCrashPolicyChanged();
|
||||
void xwaylandMaxCrashCountChanged();
|
||||
void xwaylandEavesdropsChanged();
|
||||
void nextFocusPrefersMouseChanged();
|
||||
void clickRaiseChanged();
|
||||
void autoRaiseChanged();
|
||||
|
@ -1002,6 +1019,7 @@ private:
|
|||
bool m_hideUtilityWindowsForInactive;
|
||||
XwaylandCrashPolicy m_xwaylandCrashPolicy;
|
||||
int m_xwaylandMaxCrashCount;
|
||||
XwaylandEavesdropsMode m_xwaylandEavesdrops;
|
||||
LatencyPolicy m_latencyPolicy;
|
||||
RenderTimeEstimator m_renderTimeEstimator;
|
||||
|
||||
|
|
|
@ -169,6 +169,15 @@ QVector<quint32> KeyboardInterfacePrivate::pressedKeys() const
|
|||
return keys;
|
||||
}
|
||||
|
||||
void KeyboardInterface::sendKey(quint32 key, KeyboardKeyState state, ClientConnection *client)
|
||||
{
|
||||
const QList<KeyboardInterfacePrivate::Resource *> keyboards = d->keyboardsForClient(client);
|
||||
const quint32 serial = d->seat->display()->nextSerial();
|
||||
for (KeyboardInterfacePrivate::Resource *keyboardResource : keyboards) {
|
||||
d->send_key(keyboardResource->handle, serial, d->seat->timestamp(), key, quint32(state));
|
||||
}
|
||||
}
|
||||
|
||||
void KeyboardInterface::sendKey(quint32 key, KeyboardKeyState state)
|
||||
{
|
||||
if (!d->updateKey(key, state)) {
|
||||
|
@ -179,11 +188,7 @@ void KeyboardInterface::sendKey(quint32 key, KeyboardKeyState state)
|
|||
return;
|
||||
}
|
||||
|
||||
const QList<KeyboardInterfacePrivate::Resource *> keyboards = d->keyboardsForClient(d->focusedSurface->client());
|
||||
const quint32 serial = d->seat->display()->nextSerial();
|
||||
for (KeyboardInterfacePrivate::Resource *keyboardResource : keyboards) {
|
||||
d->send_key(keyboardResource->handle, serial, d->seat->timestamp(), key, quint32(state));
|
||||
}
|
||||
sendKey(key, state, d->focusedSurface->client());
|
||||
}
|
||||
|
||||
void KeyboardInterface::sendModifiers(quint32 depressed, quint32 latched, quint32 locked, quint32 group)
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
namespace KWaylandServer
|
||||
{
|
||||
class ClientConnection;
|
||||
class SeatInterface;
|
||||
class SurfaceInterface;
|
||||
class KeyboardInterfacePrivate;
|
||||
|
@ -54,6 +55,7 @@ public:
|
|||
void setRepeatInfo(qint32 charactersPerSecond, qint32 delay);
|
||||
|
||||
void sendKey(quint32 key, KeyboardKeyState state);
|
||||
void sendKey(quint32 key, KeyboardKeyState state, ClientConnection *client);
|
||||
void sendModifiers(quint32 depressed, quint32 latched, quint32 locked, quint32 group);
|
||||
|
||||
private:
|
||||
|
|
|
@ -908,6 +908,7 @@ void SeatInterface::setFocusedKeyboardSurface(SurfaceInterface *surface)
|
|||
return;
|
||||
}
|
||||
|
||||
Q_EMIT focusedKeyboardSurfaceAboutToChange(surface);
|
||||
const quint32 serial = d->display->nextSerial();
|
||||
|
||||
if (d->globalKeyboard.focus.surface) {
|
||||
|
|
|
@ -720,6 +720,11 @@ Q_SIGNALS:
|
|||
* @see focusedTextInput
|
||||
*/
|
||||
void focusedTextInputSurfaceChanged();
|
||||
/**
|
||||
* Emitted whenever the focused keyboard is about to change.
|
||||
* @see focusedKeyboardSurface
|
||||
*/
|
||||
void focusedKeyboardSurfaceAboutToChange(SurfaceInterface *nextSurface);
|
||||
|
||||
private:
|
||||
std::unique_ptr<SeatInterfacePrivate> d;
|
||||
|
|
|
@ -15,19 +15,27 @@
|
|||
#include "cursor.h"
|
||||
#include "databridge.h"
|
||||
#include "dnd.h"
|
||||
#include "window.h"
|
||||
#include "xwaylandlauncher.h"
|
||||
#include "xwldrophandler.h"
|
||||
|
||||
#include "core/output.h"
|
||||
#include "input_event_spy.h"
|
||||
#include "keyboard_input.h"
|
||||
#include "main_wayland.h"
|
||||
#include "utils/common.h"
|
||||
#include "utils/xcbutils.h"
|
||||
#include "wayland_server.h"
|
||||
#include "waylandwindow.h"
|
||||
#include "workspace.h"
|
||||
#include "x11eventfilter.h"
|
||||
#include "xkb.h"
|
||||
#include "xwayland_logging.h"
|
||||
|
||||
#include <KSelectionOwner>
|
||||
#include <wayland/keyboard_interface.h>
|
||||
#include <wayland/seat_interface.h>
|
||||
#include <wayland/surface_interface.h>
|
||||
|
||||
#include <QAbstractEventDispatcher>
|
||||
#include <QDataStream>
|
||||
|
@ -41,8 +49,10 @@
|
|||
|
||||
#include <cerrno>
|
||||
#include <cstring>
|
||||
#include <input_event.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#include <xkbcommon/xkbcommon-keysyms.h>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
@ -73,6 +83,116 @@ bool XrandrEventFilter::event(xcb_generic_event_t *event)
|
|||
return false;
|
||||
}
|
||||
|
||||
class XwaylandInputSpy : public QObject, public KWin::InputEventSpy
|
||||
{
|
||||
public:
|
||||
XwaylandInputSpy()
|
||||
{
|
||||
connect(waylandServer()->seat(), &KWaylandServer::SeatInterface::focusedKeyboardSurfaceAboutToChange,
|
||||
this, [this](KWaylandServer::SurfaceInterface *newSurface) {
|
||||
auto keyboard = waylandServer()->seat()->keyboard();
|
||||
if (!newSurface) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (waylandServer()->xWaylandConnection() == newSurface->client()) {
|
||||
// Since this is a spy but the keyboard interface gets its normal sendKey calls through filters,
|
||||
// there can be a mismatch in both states.
|
||||
// This loop makes sure all key press events are reset before we switch back to the
|
||||
// Xwayland client and the state is correctly restored.
|
||||
for (auto it = m_states.constBegin(); it != m_states.constEnd(); ++it) {
|
||||
if (it.value() == KWaylandServer::KeyboardKeyState::Pressed) {
|
||||
keyboard->sendKey(it.key(), KWaylandServer::KeyboardKeyState::Released, waylandServer()->xWaylandConnection());
|
||||
}
|
||||
}
|
||||
m_states.clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void setMode(XwaylandEavesdropsMode mode)
|
||||
{
|
||||
static const QSet<quint32> modifierKeys = {
|
||||
Qt::Key_Control,
|
||||
Qt::Key_Shift,
|
||||
Qt::Key_Alt,
|
||||
Qt::Key_Meta,
|
||||
};
|
||||
|
||||
switch (mode) {
|
||||
case None:
|
||||
m_filter = {};
|
||||
break;
|
||||
case Modifiers:
|
||||
m_filter = [](int key, Qt::KeyboardModifiers) {
|
||||
return modifierKeys.contains(key);
|
||||
};
|
||||
break;
|
||||
case Combinations:
|
||||
m_filter = [](int key, Qt::KeyboardModifiers m) {
|
||||
return m != Qt::NoModifier || modifierKeys.contains(key);
|
||||
};
|
||||
break;
|
||||
case All:
|
||||
m_filter = [](int, Qt::KeyboardModifiers) {
|
||||
return true;
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void keyEvent(KWin::KeyEvent *event) override
|
||||
{
|
||||
if (event->isAutoRepeat()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Window *window = workspace()->activeWindow();
|
||||
if (!m_filter || !m_filter(event->key(), event->modifiers()) || (window && window->isLockScreen())) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto keyboard = waylandServer()->seat()->keyboard();
|
||||
auto surface = keyboard->focusedSurface();
|
||||
if (!surface) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto client = surface->client();
|
||||
if (waylandServer()->xWaylandConnection() != client) {
|
||||
KWaylandServer::KeyboardKeyState state{event->type() == QEvent::KeyPress};
|
||||
if (!updateKey(event->nativeScanCode(), state)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto xkb = input()->keyboard()->xkb();
|
||||
keyboard->sendModifiers(xkb->modifierState().depressed,
|
||||
xkb->modifierState().latched,
|
||||
xkb->modifierState().locked,
|
||||
xkb->currentLayout());
|
||||
|
||||
waylandServer()->seat()->keyboard()->sendKey(event->nativeScanCode(), state, waylandServer()->xWaylandConnection());
|
||||
}
|
||||
}
|
||||
|
||||
bool updateKey(quint32 key, KWaylandServer::KeyboardKeyState state)
|
||||
{
|
||||
auto it = m_states.find(key);
|
||||
if (it == m_states.end()) {
|
||||
m_states.insert(key, state);
|
||||
return true;
|
||||
}
|
||||
if (it.value() == state) {
|
||||
return false;
|
||||
}
|
||||
it.value() = state;
|
||||
return true;
|
||||
}
|
||||
|
||||
QHash<quint32, KWaylandServer::KeyboardKeyState> m_states;
|
||||
std::function<bool(int key, Qt::KeyboardModifiers)> m_filter;
|
||||
};
|
||||
|
||||
Xwayland::Xwayland(Application *app)
|
||||
: m_app(app)
|
||||
, m_launcher(new XwaylandLauncher(this))
|
||||
|
@ -205,6 +325,33 @@ void Xwayland::handleXwaylandReady()
|
|||
|
||||
delete m_xrandrEventsFilter;
|
||||
m_xrandrEventsFilter = new XrandrEventFilter(this);
|
||||
|
||||
refreshEavesdropping();
|
||||
connect(options, &Options::xwaylandEavesdropsChanged, this, &Xwayland::refreshEavesdropping);
|
||||
}
|
||||
|
||||
void Xwayland::refreshEavesdropping()
|
||||
{
|
||||
if (!waylandServer()->seat()->keyboard()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool enabled = options->xwaylandEavesdrops() != None;
|
||||
if (enabled == bool(m_inputSpy)) {
|
||||
if (m_inputSpy) {
|
||||
m_inputSpy->setMode(options->xwaylandEavesdrops());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
m_inputSpy.reset(new XwaylandInputSpy);
|
||||
input()->installInputEventSpy(m_inputSpy.get());
|
||||
m_inputSpy->setMode(options->xwaylandEavesdrops());
|
||||
} else {
|
||||
input()->uninstallInputEventSpy(m_inputSpy.get());
|
||||
m_inputSpy.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void Xwayland::updatePrimary()
|
||||
|
|
|
@ -27,6 +27,7 @@ class Application;
|
|||
namespace Xwl
|
||||
{
|
||||
class XrandrEventFilter;
|
||||
class XwaylandInputSpy;
|
||||
class XwaylandLauncher;
|
||||
class DataBridge;
|
||||
|
||||
|
@ -69,6 +70,7 @@ private:
|
|||
void installSocketNotifier();
|
||||
void uninstallSocketNotifier();
|
||||
void updatePrimary();
|
||||
void refreshEavesdropping();
|
||||
|
||||
bool createX11Connection();
|
||||
void destroyX11Connection();
|
||||
|
@ -83,6 +85,7 @@ private:
|
|||
|
||||
XrandrEventFilter *m_xrandrEventsFilter = nullptr;
|
||||
XwaylandLauncher *m_launcher;
|
||||
std::unique_ptr<XwaylandInputSpy> m_inputSpy;
|
||||
|
||||
Q_DISABLE_COPY(Xwayland)
|
||||
};
|
||||
|
|
|
@ -90,6 +90,7 @@ private Q_SLOTS:
|
|||
|
||||
private:
|
||||
void maybeDestroyReadyNotifier();
|
||||
|
||||
bool startInternal();
|
||||
void stopInternal();
|
||||
void restartInternal();
|
||||
|
|
Loading…
Reference in a new issue