diff --git a/autotests/integration/CMakeLists.txt b/autotests/integration/CMakeLists.txt --- a/autotests/integration/CMakeLists.txt +++ b/autotests/integration/CMakeLists.txt @@ -43,6 +43,7 @@ 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 --- /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.h b/keyboard_input.h --- a/keyboard_input.h +++ b/keyboard_input.h @@ -29,6 +29,8 @@ #include Q_DECLARE_LOGGING_CATEGORY(KWIN_XKB) +#include + class QWindow; struct xkb_context; struct xkb_keymap; @@ -58,6 +60,9 @@ 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 @@ xkb_compose_state *state = nullptr; } m_compose; LEDs m_leds; + KSharedConfigPtr m_config; }; class KWIN_EXPORT KeyboardInputRedirection : public QObject diff --git a/keyboard_input.cpp b/keyboard_input.cpp --- a/keyboard_input.cpp +++ b/keyboard_input.cpp @@ -168,7 +168,10 @@ 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 @@ { 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_layout.cpp b/keyboard_layout.cpp --- a/keyboard_layout.cpp +++ b/keyboard_layout.cpp @@ -140,10 +140,10 @@ void KeyboardLayout::reconfigure() { - m_xkb->reconfigure(); if (m_config) { m_config->reparseConfiguration(); } + m_xkb->reconfigure(); resetLayout(); } diff --git a/main.h b/main.h --- a/main.h +++ b/main.h @@ -55,6 +55,7 @@ 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 @@ 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 @@ 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; diff --git a/main.cpp b/main.cpp --- a/main.cpp +++ b/main.cpp @@ -104,6 +104,7 @@ , m_eventFilter(new XcbEventFilter()) , m_configLock(false) , m_config() + , m_kxkbConfig() , m_operationMode(mode) { qRegisterMetaType("Options::WindowOperation"); @@ -144,6 +145,9 @@ //config->setReadOnly( true ); m_config->reparseConfiguration(); } + if (!m_kxkbConfig) { + m_kxkbConfig = KSharedConfig::openConfig(QStringLiteral("kxkbrc"), KConfig::NoGlobals); + } performStartup(); }