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/D17304icc-effect-5.17.5
parent
070b46f455
commit
a0b482cbab
|
@ -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)
|
||||
|
|
|
@ -0,0 +1,285 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2018 Martin Flöser <mgraesslin@kde.org>
|
||||
|
||||
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"
|
|
@ -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());
|
||||
}
|
||||
|
|
15
input.cpp
15
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()) {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
Loading…
Reference in New Issue