From 8d9c4acf4dee6e2fdae783e441c496960a827d11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Fri, 27 Jan 2017 17:15:32 +0100 Subject: [PATCH] Provide kxbk config through kwinApp Summary: So far KWin parsed the kxbkrc at multiple places (once in Xkb, once in KeyboardLayout). This is now replaced by one KSharedConfigPtr hold by kwinApp, just like the normal kwinrc. The KSharedConfigPtr is now passed to Xkb. As a nice side effect this makes it easier to test keyboard layout changes as we can now properly mock the keyboard configuration. Thus this change also comes with an autotest for loading keyboard layout configuration. This is becoming more and more a need as we start getting bug reports for layout specific issues like global shortcuts not working with Greek layout. Reviewers: #kwin, #plasma_on_wayland Subscribers: plasma-devel, kwin Tags: #plasma_on_wayland, #kwin Differential Revision: https://phabricator.kde.org/D4315 --- autotests/integration/CMakeLists.txt | 1 + .../integration/keyboard_layout_test.cpp | 103 ++++++++++++++++++ keyboard_input.cpp | 10 +- keyboard_input.h | 6 + keyboard_layout.cpp | 2 +- main.cpp | 4 + main.h | 9 ++ 7 files changed, 132 insertions(+), 3 deletions(-) create mode 100644 autotests/integration/keyboard_layout_test.cpp diff --git a/autotests/integration/CMakeLists.txt b/autotests/integration/CMakeLists.txt index 6134011749..73cd8b6d46 100644 --- a/autotests/integration/CMakeLists.txt +++ b/autotests/integration/CMakeLists.txt @@ -43,6 +43,7 @@ integrationTest(NAME testTabBox SRCS tabbox_test.cpp) integrationTest(NAME testGlobalShortcuts SRCS globalshortcuts_test.cpp) integrationTest(NAME testWindowSelection SRCS window_selection_test.cpp) integrationTest(NAME testPointerConstraints SRCS pointer_constraints_test.cpp) +integrationTest(NAME testKeyboardLayout SRCS keyboard_layout_test.cpp) if (XCB_ICCCM_FOUND) integrationTest(NAME testMoveResize SRCS move_resize_window_test.cpp LIBS XCB::ICCCM) diff --git a/autotests/integration/keyboard_layout_test.cpp b/autotests/integration/keyboard_layout_test.cpp new file mode 100644 index 0000000000..849057e9e1 --- /dev/null +++ b/autotests/integration/keyboard_layout_test.cpp @@ -0,0 +1,103 @@ +/******************************************************************** +KWin - the KDE window manager +This file is part of the KDE project. + +Copyright (C) 2017 Martin Gräßlin + +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 . +*********************************************************************/ +#include "kwin_wayland_test.h" +#include "keyboard_input.h" +#include "platform.h" +#include "wayland_server.h" + +#include + +#include +#include + +using namespace KWin; +using namespace KWayland::Client; + +static const QString s_socketName = QStringLiteral("wayland_test_kwin_keyboard_laout-0"); + +class KeyboardLayoutTest : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void initTestCase(); + void init(); + void cleanup(); + + void testReconfigure(); +}; + +void KeyboardLayoutTest::initTestCase() +{ + QSignalSpy workspaceCreatedSpy(kwinApp(), &Application::workspaceCreated); + QVERIFY(workspaceCreatedSpy.isValid()); + kwinApp()->platform()->setInitialWindowSize(QSize(1280, 1024)); + QVERIFY(waylandServer()->init(s_socketName.toLocal8Bit())); + + kwinApp()->setConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig)); + kwinApp()->setKxkbConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig)); + + kwinApp()->start(); + QVERIFY(workspaceCreatedSpy.wait()); + waylandServer()->initWorkspace(); +} + +void KeyboardLayoutTest::init() +{ +} + +void KeyboardLayoutTest::cleanup() +{ +} + +void KeyboardLayoutTest::testReconfigure() +{ + // verifies that we can change the keymap + + // default should be a keymap with only us layout + auto xkb = input()->keyboard()->xkb(); + QCOMPARE(xkb->numberOfLayouts(), 1u); + QCOMPARE(xkb->layoutName(), QStringLiteral("English (US)")); + auto layouts = xkb->layoutNames(); + QCOMPARE(layouts.size(), 1); + QVERIFY(layouts.contains(0)); + QCOMPARE(layouts[0], QStringLiteral("English (US)")); + + // create a new keymap + KConfigGroup layoutGroup = kwinApp()->kxkbConfig()->group("Layout"); + layoutGroup.writeEntry("LayoutList", QStringLiteral("de,us")); + layoutGroup.sync(); + + // create DBus signal to reload + QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/Layouts"), QStringLiteral("org.kde.keyboard"), QStringLiteral("reloadConfig")); + QDBusConnection::sessionBus().send(message); + // now we should have two layouts + QTRY_COMPARE(xkb->numberOfLayouts(), 2u); + // default layout is German + QCOMPARE(xkb->layoutName(), QStringLiteral("German")); + layouts = xkb->layoutNames(); + QCOMPARE(layouts.size(), 2); + QVERIFY(layouts.contains(0)); + QVERIFY(layouts.contains(1)); + QCOMPARE(layouts[0], QStringLiteral("German")); + QCOMPARE(layouts[1], QStringLiteral("English (US)")); +} + +WAYLANDTEST_MAIN(KeyboardLayoutTest) +#include "keyboard_layout_test.moc" diff --git a/keyboard_input.cpp b/keyboard_input.cpp index 201fbbece0..94dbafc07e 100644 --- a/keyboard_input.cpp +++ b/keyboard_input.cpp @@ -168,7 +168,10 @@ void Xkb::reconfigure() xkb_keymap *Xkb::loadKeymapFromConfig() { // load config - const KConfigGroup config = KSharedConfig::openConfig(QStringLiteral("kxkbrc"), KConfig::NoGlobals)->group("Layout"); + if (!m_config) { + return nullptr; + } + const KConfigGroup config = m_config->group("Layout"); const QByteArray model = config.readEntry("Model", "pc104").toLocal8Bit(); const QByteArray layout = config.readEntry("LayoutList", "").toLocal8Bit(); const QByteArray options = config.readEntry("Options", "").toLocal8Bit(); @@ -598,11 +601,14 @@ void KeyboardInputRedirection::init() { Q_ASSERT(!m_inited); m_inited = true; + const auto config = kwinApp()->kxkbConfig(); + m_xkb->setConfig(config); + m_input->installInputEventSpy(new KeyStateChangedSpy(m_input)); m_modifiersChangedSpy = new ModifiersChangedSpy(m_input); m_input->installInputEventSpy(m_modifiersChangedSpy); m_keyboardLayout = new KeyboardLayout(m_xkb.data()); - m_keyboardLayout->setConfig(KSharedConfig::openConfig(QStringLiteral("kxkbrc"), KConfig::NoGlobals)); + m_keyboardLayout->setConfig(config); m_keyboardLayout->init(); m_input->installInputEventSpy(m_keyboardLayout); diff --git a/keyboard_input.h b/keyboard_input.h index ea6977628c..cd2254d9fe 100644 --- a/keyboard_input.h +++ b/keyboard_input.h @@ -29,6 +29,8 @@ along with this program. If not, see . #include Q_DECLARE_LOGGING_CATEGORY(KWIN_XKB) +#include + class QWindow; struct xkb_context; struct xkb_keymap; @@ -58,6 +60,9 @@ class KWIN_EXPORT Xkb public: Xkb(InputRedirection *input); ~Xkb(); + void setConfig(KSharedConfigPtr config) { + m_config = config; + } void reconfigure(); void installKeymap(int fd, uint32_t size); @@ -136,6 +141,7 @@ private: xkb_compose_state *state = nullptr; } m_compose; LEDs m_leds; + KSharedConfigPtr m_config; }; class KWIN_EXPORT KeyboardInputRedirection : public QObject diff --git a/keyboard_layout.cpp b/keyboard_layout.cpp index df152546ea..760f6fa116 100644 --- a/keyboard_layout.cpp +++ b/keyboard_layout.cpp @@ -140,10 +140,10 @@ void KeyboardLayout::switchToLayout(xkb_layout_index_t index) void KeyboardLayout::reconfigure() { - m_xkb->reconfigure(); if (m_config) { m_config->reparseConfiguration(); } + m_xkb->reconfigure(); resetLayout(); } diff --git a/main.cpp b/main.cpp index 65fc150388..6e045ef679 100644 --- a/main.cpp +++ b/main.cpp @@ -104,6 +104,7 @@ Application::Application(Application::OperationMode mode, int &argc, char **argv , m_eventFilter(new XcbEventFilter()) , m_configLock(false) , m_config() + , m_kxkbConfig() , m_operationMode(mode) { qRegisterMetaType("Options::WindowOperation"); @@ -144,6 +145,9 @@ void Application::start() //config->setReadOnly( true ); m_config->reparseConfiguration(); } + if (!m_kxkbConfig) { + m_kxkbConfig = KSharedConfig::openConfig(QStringLiteral("kxkbrc"), KConfig::NoGlobals); + } performStartup(); } diff --git a/main.h b/main.h index 0758da8405..e9150be429 100644 --- a/main.h +++ b/main.h @@ -55,6 +55,7 @@ class KWIN_EXPORT Application : public QApplication Q_PROPERTY(void *x11Connection READ x11Connection NOTIFY x11ConnectionChanged) Q_PROPERTY(int x11ScreenNumber READ x11ScreenNumber CONSTANT) Q_PROPERTY(KSharedConfigPtr config READ config WRITE setConfig) + Q_PROPERTY(KSharedConfigPtr kxkbConfig READ kxkbConfig WRITE setKxkbConfig) public: /** * @brief This enum provides the various operation modes of KWin depending on the available @@ -88,6 +89,13 @@ public: m_config = config; } + KSharedConfigPtr kxkbConfig() const { + return m_kxkbConfig; + } + void setKxkbConfig(KSharedConfigPtr config) { + m_kxkbConfig = config; + } + void start(); /** * @brief The operation mode used by KWin. @@ -223,6 +231,7 @@ private: QScopedPointer m_eventFilter; bool m_configLock; KSharedConfigPtr m_config; + KSharedConfigPtr m_kxkbConfig; OperationMode m_operationMode; xcb_timestamp_t m_x11Time = XCB_TIME_CURRENT_TIME; xcb_window_t m_rootWindow = XCB_WINDOW_NONE;