aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Flöser <[email protected]>2018-12-02 13:09:37 +0100
committerMartin Flöser <[email protected]>2018-12-13 19:36:28 +0100
commita0b482cbabbc715176c82304ce46a3abd065153b (patch)
tree8e1b14fdb2f0b3fb7ab257a178852682e181bc69
parent070b46f455667d0f9ddbccffc1a43f6a44a25f6d (diff)
Introduce a command line option to disable any kind of global shortcuts
Summary: This command line option is useful for KWin in embedded use case. That is when KWin is just used as a compositor for one application instead of a complete desktop environment. In such a setup global shortcuts are not wanted and interfere with the application. E.g. one does not want Alt+F4 to close the window, that would render the system unusable. This change introduces a command line option and disables the following event filters and spies: * global shortcuts * modifier only shortcuts * terminate session * virtual terminal switching * screen edges KGlobalAccel still gets inited, otherwise the (non-functional) binary would be launched when KWin registers it's global shortcuts. Test Plan: New test added based on existing tests for the global shortcuts, ctest passes Reviewers: #kwin Subscribers: kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D17304
-rw-r--r--autotests/integration/CMakeLists.txt1
-rw-r--r--autotests/integration/no_global_shortcuts_test.cpp285
-rw-r--r--autotests/integration/start_test.cpp1
-rw-r--r--input.cpp15
-rw-r--r--keyboard_input.cpp4
-rw-r--r--main_wayland.cpp7
-rw-r--r--wayland_server.cpp5
-rw-r--r--wayland_server.h8
8 files changed, 320 insertions, 6 deletions
diff --git a/autotests/integration/CMakeLists.txt b/autotests/integration/CMakeLists.txt
index b4a56a3..407964b 100644
--- a/autotests/integration/CMakeLists.txt
+++ b/autotests/integration/CMakeLists.txt
@@ -58,6 +58,7 @@ integrationTest(WAYLAND_ONLY NAME testIdleInhibition SRCS idle_inhibition_test.c
integrationTest(WAYLAND_ONLY NAME testColorCorrectNightColor SRCS colorcorrect_nightcolor_test.cpp)
integrationTest(WAYLAND_ONLY NAME testDontCrashCursorPhysicalSizeEmpty SRCS dont_crash_cursor_physical_size_empty.cpp)
integrationTest(WAYLAND_ONLY NAME testDontCrashReinitializeCompositor SRCS dont_crash_reinitialize_compositor.cpp)
+integrationTest(WAYLAND_ONLY NAME testNoGlobalShortcuts SRCS no_global_shortcuts_test.cpp)
if (XCB_ICCCM_FOUND)
integrationTest(NAME testMoveResize SRCS move_resize_window_test.cpp LIBS XCB::ICCCM)
diff --git a/autotests/integration/no_global_shortcuts_test.cpp b/autotests/integration/no_global_shortcuts_test.cpp
new file mode 100644
index 0000000..10d0a4f
--- /dev/null
+++ b/autotests/integration/no_global_shortcuts_test.cpp
@@ -0,0 +1,285 @@
+/********************************************************************
+KWin - the KDE window manager
+This file is part of the KDE project.
+
+Copyright (C) 2018 Martin Flöser <[email protected]>
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*********************************************************************/
+#include "kwin_wayland_test.h"
+#include "cursor.h"
+#include "input.h"
+#include "keyboard_input.h"
+#include "platform.h"
+#include "screenedge.h"
+#include "screens.h"
+#include "wayland_server.h"
+#include "workspace.h"
+
+#include <KConfigGroup>
+#include <KGlobalAccel>
+
+#include <QDBusConnection>
+
+#include <linux/input.h>
+
+using namespace KWin;
+using namespace KWayland::Client;
+
+static const QString s_socketName = QStringLiteral("wayland_test_kwin_no_global_shortcuts-0");
+static const QString s_serviceName = QStringLiteral("org.kde.KWin.Test.ModifierOnlyShortcut");
+static const QString s_path = QStringLiteral("/Test");
+
+Q_DECLARE_METATYPE(KWin::ElectricBorder)
+
+/**
+ * This test verifies the NoGlobalShortcuts initialization flag
+ **/
+class NoGlobalShortcutsTest : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void initTestCase();
+ void init();
+ void cleanup();
+
+ void testTrigger_data();
+ void testTrigger();
+ void testKGlobalAccel();
+ void testPointerShortcut();
+ void testAxisShortcut_data();
+ void testAxisShortcut();
+ void testScreenEdge();
+};
+
+class Target : public QObject
+{
+ Q_OBJECT
+ Q_CLASSINFO("D-Bus Interface", "org.kde.KWin.Test.ModifierOnlyShortcut")
+
+public:
+ Target();
+ virtual ~Target();
+
+public Q_SLOTS:
+ Q_SCRIPTABLE void shortcut();
+
+Q_SIGNALS:
+ void shortcutTriggered();
+};
+
+Target::Target()
+ : QObject()
+{
+ QDBusConnection::sessionBus().registerService(s_serviceName);
+ QDBusConnection::sessionBus().registerObject(s_path, s_serviceName, this, QDBusConnection::ExportScriptableSlots);
+}
+
+Target::~Target()
+{
+ QDBusConnection::sessionBus().unregisterObject(s_path);
+ QDBusConnection::sessionBus().unregisterService(s_serviceName);
+}
+
+void Target::shortcut()
+{
+ emit shortcutTriggered();
+}
+
+void NoGlobalShortcutsTest::initTestCase()
+{
+ qRegisterMetaType<KWin::ElectricBorder>("ElectricBorder");
+ QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated);
+ QVERIFY(workspaceCreatedSpy.isValid());
+ kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024));
+ QVERIFY(waylandServer()->init(s_socketName.toLocal8Bit(), KWin::WaylandServer::InitalizationFlag::NoGlobalShortcuts));
+
+ kwinApp()->setConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig));
+ qputenv("KWIN_XKB_DEFAULT_KEYMAP", "1");
+ qputenv("XKB_DEFAULT_RULES", "evdev");
+
+ kwinApp()->start();
+ QVERIFY(workspaceCreatedSpy.wait());
+ waylandServer()->initWorkspace();
+}
+
+void NoGlobalShortcutsTest::init()
+{
+ screens()->setCurrent(0);
+ KWin::Cursor::setPos(QPoint(640, 512));
+}
+
+void NoGlobalShortcutsTest::cleanup()
+{
+}
+
+void NoGlobalShortcutsTest::testTrigger_data()
+{
+ QTest::addColumn<QStringList>("metaConfig");
+ QTest::addColumn<QStringList>("altConfig");
+ QTest::addColumn<QStringList>("controlConfig");
+ QTest::addColumn<QStringList>("shiftConfig");
+ QTest::addColumn<int>("modifier");
+ QTest::addColumn<QList<int>>("nonTriggeringMods");
+
+ const QStringList trigger = QStringList{s_serviceName, s_path, s_serviceName, QStringLiteral("shortcut")};
+ const QStringList e = QStringList();
+
+ QTest::newRow("leftMeta") << trigger << e << e << e << KEY_LEFTMETA << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
+ QTest::newRow("rightMeta") << trigger << e << e << e << KEY_RIGHTMETA << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
+ QTest::newRow("leftAlt") << e << trigger << e << e << KEY_LEFTALT << QList<int>{KEY_LEFTMETA, KEY_RIGHTMETA, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
+ QTest::newRow("rightAlt") << e << trigger << e << e << KEY_RIGHTALT << QList<int>{KEY_LEFTMETA, KEY_RIGHTMETA, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
+ QTest::newRow("leftControl") << e << e << trigger << e << KEY_LEFTCTRL << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
+ QTest::newRow("rightControl") << e << e << trigger << e << KEY_RIGHTCTRL << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
+ QTest::newRow("leftShift") << e << e << e << trigger << KEY_LEFTSHIFT << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTMETA, KEY_RIGHTMETA};
+ QTest::newRow("rightShift") << e << e << e << trigger <<KEY_RIGHTSHIFT << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTMETA, KEY_RIGHTMETA};
+}
+
+void NoGlobalShortcutsTest::testTrigger()
+{
+ // test based on ModifierOnlyShortcutTest::testTrigger
+ Target target;
+ QSignalSpy triggeredSpy(&target, &Target::shortcutTriggered);
+ QVERIFY(triggeredSpy.isValid());
+
+ KConfigGroup group = kwinApp()->config()->group("ModifierOnlyShortcuts");
+ QFETCH(QStringList, metaConfig);
+ QFETCH(QStringList, altConfig);
+ QFETCH(QStringList, shiftConfig);
+ QFETCH(QStringList, controlConfig);
+ group.writeEntry("Meta", metaConfig);
+ group.writeEntry("Alt", altConfig);
+ group.writeEntry("Shift", shiftConfig);
+ group.writeEntry("Control", controlConfig);
+ group.sync();
+ workspace()->slotReconfigure();
+
+ // configured shortcut should trigger
+ quint32 timestamp = 1;
+ QFETCH(int, modifier);
+ kwinApp()->platform()->keyboardKeyPressed(modifier, timestamp++);
+ kwinApp()->platform()->keyboardKeyReleased(modifier, timestamp++);
+ QCOMPARE(triggeredSpy.count(), 0);
+
+ // the other shortcuts should not trigger
+ QFETCH(QList<int>, nonTriggeringMods);
+ for (auto it = nonTriggeringMods.constBegin(), end = nonTriggeringMods.constEnd(); it != end; it++) {
+ kwinApp()->platform()->keyboardKeyPressed(*it, timestamp++);
+ kwinApp()->platform()->keyboardKeyReleased(*it, timestamp++);
+ QCOMPARE(triggeredSpy.count(), 0);
+ }
+}
+
+void NoGlobalShortcutsTest::testKGlobalAccel()
+{
+ QScopedPointer<QAction> action(new QAction(nullptr));
+ action->setProperty("componentName", QStringLiteral(KWIN_NAME));
+ action->setObjectName(QStringLiteral("globalshortcuts-test-meta-shift-w"));
+ QSignalSpy triggeredSpy(action.data(), &QAction::triggered);
+ QVERIFY(triggeredSpy.isValid());
+ KGlobalAccel::self()->setShortcut(action.data(), QList<QKeySequence>{Qt::META + Qt::SHIFT + Qt::Key_W}, KGlobalAccel::NoAutoloading);
+ input()->registerShortcut(Qt::META + Qt::SHIFT + Qt::Key_W, action.data());
+
+ // press meta+shift+w
+ quint32 timestamp = 0;
+ kwinApp()->platform()->keyboardKeyPressed(KEY_LEFTMETA, timestamp++);
+ QCOMPARE(input()->keyboardModifiers(), Qt::MetaModifier);
+ kwinApp()->platform()->keyboardKeyPressed(KEY_LEFTSHIFT, timestamp++);
+ QCOMPARE(input()->keyboardModifiers(), Qt::ShiftModifier | Qt::MetaModifier);
+ kwinApp()->platform()->keyboardKeyPressed(KEY_W, timestamp++);
+ kwinApp()->platform()->keyboardKeyReleased(KEY_W, timestamp++);
+
+ // release meta+shift
+ kwinApp()->platform()->keyboardKeyReleased(KEY_LEFTSHIFT, timestamp++);
+ kwinApp()->platform()->keyboardKeyReleased(KEY_LEFTMETA, timestamp++);
+
+ QVERIFY(!triggeredSpy.wait());
+ QCOMPARE(triggeredSpy.count(), 0);
+}
+
+void NoGlobalShortcutsTest::testPointerShortcut()
+{
+ // based on LockScreenTest::testPointerShortcut
+ QScopedPointer<QAction> action(new QAction(nullptr));
+ QSignalSpy actionSpy(action.data(), &QAction::triggered);
+ QVERIFY(actionSpy.isValid());
+ input()->registerPointerShortcut(Qt::MetaModifier, Qt::LeftButton, action.data());
+
+ // try to trigger the shortcut
+ quint32 timestamp = 1;
+ kwinApp()->platform()->keyboardKeyPressed(KEY_LEFTMETA, timestamp++);
+ kwinApp()->platform()->pointerButtonPressed(BTN_LEFT, timestamp++);
+ QCoreApplication::instance()->processEvents();
+ QCOMPARE(actionSpy.count(), 0);
+ kwinApp()->platform()->pointerButtonReleased(BTN_LEFT, timestamp++);
+ kwinApp()->platform()->keyboardKeyReleased(KEY_LEFTMETA, timestamp++);
+ QCoreApplication::instance()->processEvents();
+ QCOMPARE(actionSpy.count(), 0);
+}
+
+void NoGlobalShortcutsTest::testAxisShortcut_data()
+{
+ QTest::addColumn<Qt::Orientation>("direction");
+ QTest::addColumn<int>("sign");
+
+ QTest::newRow("up") << Qt::Vertical << 1;
+ QTest::newRow("down") << Qt::Vertical << -1;
+ QTest::newRow("left") << Qt::Horizontal << 1;
+ QTest::newRow("right") << Qt::Horizontal << -1;
+}
+
+void NoGlobalShortcutsTest::testAxisShortcut()
+{
+ // based on LockScreenTest::testAxisShortcut
+ QScopedPointer<QAction> action(new QAction(nullptr));
+ QSignalSpy actionSpy(action.data(), &QAction::triggered);
+ QVERIFY(actionSpy.isValid());
+ QFETCH(Qt::Orientation, direction);
+ QFETCH(int, sign);
+ PointerAxisDirection axisDirection = PointerAxisUp;
+ if (direction == Qt::Vertical) {
+ axisDirection = sign > 0 ? PointerAxisUp : PointerAxisDown;
+ } else {
+ axisDirection = sign > 0 ? PointerAxisLeft : PointerAxisRight;
+ }
+ input()->registerAxisShortcut(Qt::MetaModifier, axisDirection, action.data());
+
+ // try to trigger the shortcut
+ quint32 timestamp = 1;
+ kwinApp()->platform()->keyboardKeyPressed(KEY_LEFTMETA, timestamp++);
+ if (direction == Qt::Vertical)
+ kwinApp()->platform()->pointerAxisVertical(sign * 5.0, timestamp++);
+ else
+ kwinApp()->platform()->pointerAxisHorizontal(sign * 5.0, timestamp++);
+ QCoreApplication::instance()->processEvents();
+ QCOMPARE(actionSpy.count(), 0);
+ kwinApp()->platform()->keyboardKeyReleased(KEY_LEFTMETA, timestamp++);
+ QCoreApplication::instance()->processEvents();
+ QCOMPARE(actionSpy.count(), 0);
+}
+
+void NoGlobalShortcutsTest::testScreenEdge()
+{
+ // based on LockScreenTest::testScreenEdge
+ QSignalSpy screenEdgeSpy(ScreenEdges::self(), &ScreenEdges::approaching);
+ QVERIFY(screenEdgeSpy.isValid());
+ QCOMPARE(screenEdgeSpy.count(), 0);
+
+ quint32 timestamp = 1;
+ kwinApp()->platform()->pointerMotion({5, 5}, timestamp++);
+ QCOMPARE(screenEdgeSpy.count(), 0);
+}
+
+WAYLANDTEST_MAIN(NoGlobalShortcutsTest)
+#include "no_global_shortcuts_test.moc"
diff --git a/autotests/integration/start_test.cpp b/autotests/integration/start_test.cpp
index 894d077..7ad3a79 100644
--- a/autotests/integration/start_test.cpp
+++ b/autotests/integration/start_test.cpp
@@ -52,6 +52,7 @@ void StartTest::initTestCase()
QVERIFY(workspaceCreatedSpy.isValid());
kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024));
QVERIFY(waylandServer()->init(s_socketName.toLocal8Bit()));
+ QVERIFY(waylandServer()->hasGlobalShortcutSupport());
kwinApp()->start();
QVERIFY(workspaceCreatedSpy.wait());
}
diff --git a/input.cpp b/input.cpp
index d35333f..8f8517e 100644
--- a/input.cpp
+++ b/input.cpp
@@ -1751,25 +1751,32 @@ void InputRedirection::setupWorkspace()
void InputRedirection::setupInputFilters()
{
- if (LogindIntegration::self()->hasSessionControl()) {
+ const bool hasGlobalShortcutSupport = !waylandServer() || waylandServer()->hasGlobalShortcutSupport();
+ if (LogindIntegration::self()->hasSessionControl() && hasGlobalShortcutSupport) {
installInputEventFilter(new VirtualTerminalFilter);
}
if (waylandServer()) {
installInputEventSpy(new TouchHideCursorSpy);
- installInputEventFilter(new TerminateServerFilter);
+ if (hasGlobalShortcutSupport) {
+ installInputEventFilter(new TerminateServerFilter);
+ }
installInputEventFilter(new DragAndDropInputFilter);
installInputEventFilter(new LockScreenFilter);
installInputEventFilter(new PopupInputFilter);
m_windowSelector = new WindowSelectorFilter;
installInputEventFilter(m_windowSelector);
}
- installInputEventFilter(new ScreenEdgeInputFilter);
+ if (hasGlobalShortcutSupport) {
+ installInputEventFilter(new ScreenEdgeInputFilter);
+ }
installInputEventFilter(new EffectsFilter);
installInputEventFilter(new MoveResizeFilter);
#ifdef KWIN_BUILD_TABBOX
installInputEventFilter(new TabBoxInputFilter);
#endif
- installInputEventFilter(new GlobalShortcutFilter);
+ if (hasGlobalShortcutSupport) {
+ installInputEventFilter(new GlobalShortcutFilter);
+ }
installInputEventFilter(new DecorationEventFilter);
installInputEventFilter(new InternalWindowEventFilter);
if (waylandServer()) {
diff --git a/keyboard_input.cpp b/keyboard_input.cpp
index 243fe30..8564149 100644
--- a/keyboard_input.cpp
+++ b/keyboard_input.cpp
@@ -122,7 +122,9 @@ void KeyboardInputRedirection::init()
m_keyboardLayout->init();
m_input->installInputEventSpy(m_keyboardLayout);
- m_input->installInputEventSpy(new ModifierOnlyShortcuts);
+ if (waylandServer()->hasGlobalShortcutSupport()) {
+ m_input->installInputEventSpy(new ModifierOnlyShortcuts);
+ }
KeyboardRepeat *keyRepeatSpy = new KeyboardRepeat(m_xkb.data());
connect(keyRepeatSpy, &KeyboardRepeat::keyRepeat, this,
diff --git a/main_wayland.cpp b/main_wayland.cpp
index 199ebf7..ce25dcc 100644
--- a/main_wayland.cpp
+++ b/main_wayland.cpp
@@ -676,6 +676,10 @@ int main(int argc, char * argv[])
i18n("Starts the session without lock screen support."));
parser.addOption(noScreenLockerOption);
+ QCommandLineOption noGlobalShortcutsOption(QStringLiteral("no-global-shortcuts"),
+ i18n("Starts the session without global shortcuts support."));
+ parser.addOption(noScreenLockerOption);
+
QCommandLineOption exitWithSessionOption(QStringLiteral("exit-with-session"),
i18n("Exit after the session application, which is started by KWin, closed."),
QStringLiteral("/path/to/session"));
@@ -792,6 +796,9 @@ int main(int argc, char * argv[])
} else if (parser.isSet(noScreenLockerOption)) {
flags = KWin::WaylandServer::InitalizationFlag::NoLockScreenIntegration;
}
+ if (parser.isSet(noGlobalShortcutsOption)) {
+ flags |= KWin::WaylandServer::InitalizationFlag::NoGlobalShortcuts;
+ }
if (!server->init(parser.value(waylandSocketOption).toUtf8(), flags)) {
std::cerr << "FATAL ERROR: could not create Wayland server" << std::endl;
return 1;
diff --git a/wayland_server.cpp b/wayland_server.cpp
index bd49624..afd86fa 100644
--- a/wayland_server.cpp
+++ b/wayland_server.cpp
@@ -795,6 +795,11 @@ bool WaylandServer::hasScreenLockerIntegration() const
return !m_initFlags.testFlag(InitalizationFlag::NoLockScreenIntegration);
}
+bool WaylandServer::hasGlobalShortcutSupport() const
+{
+ return !m_initFlags.testFlag(InitalizationFlag::NoGlobalShortcuts);
+}
+
void WaylandServer::simulateUserActivity()
{
if (m_idle) {
diff --git a/wayland_server.h b/wayland_server.h
index bc0a589..f5f3805 100644
--- a/wayland_server.h
+++ b/wayland_server.h
@@ -79,7 +79,8 @@ public:
enum class InitalizationFlag {
NoOptions = 0x0,
LockScreen = 0x1,
- NoLockScreenIntegration = 0x2
+ NoLockScreenIntegration = 0x2,
+ NoGlobalShortcuts = 0x4
};
Q_DECLARE_FLAGS(InitalizationFlags, InitalizationFlag)
@@ -153,6 +154,11 @@ public:
**/
bool hasScreenLockerIntegration() const;
+ /**
+ * @returns whether any kind of global shortcuts are supported.
+ **/
+ bool hasGlobalShortcutSupport() const;
+
void createInternalConnection();
void initWorkspace();