Warning, file /plasma/kwin/autotests/integration/keyboard_layout_test.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2017 Martin Gräßlin <mgraesslin@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #include "kwin_wayland_test.h"
0010 
0011 #include "core/outputbackend.h"
0012 #include "keyboard_input.h"
0013 #include "keyboard_layout.h"
0014 #include "virtualdesktops.h"
0015 #include "wayland_server.h"
0016 #include "window.h"
0017 #include "workspace.h"
0018 #include "xkb.h"
0019 
0020 #include <KConfigGroup>
0021 #include <KGlobalAccel>
0022 
0023 #include <KWayland/Client/surface.h>
0024 
0025 #include <QAction>
0026 #include <QDBusConnection>
0027 #include <QDBusConnectionInterface>
0028 #include <QDBusMessage>
0029 #include <QDBusPendingCall>
0030 
0031 #include <linux/input.h>
0032 
0033 using namespace KWin;
0034 
0035 static const QString s_socketName = QStringLiteral("wayland_test_kwin_keyboard_laout-0");
0036 
0037 class KeyboardLayoutTest : public QObject
0038 {
0039     Q_OBJECT
0040 public:
0041     KeyboardLayoutTest()
0042         : layoutsReconfiguredSpy(this, &KeyboardLayoutTest::layoutListChanged)
0043         , layoutChangedSpy(this, &KeyboardLayoutTest::layoutChanged)
0044     {
0045 
0046         QVERIFY(QDBusConnection::sessionBus().connect(QStringLiteral("org.kde.keyboard"), QStringLiteral("/Layouts"), QStringLiteral("org.kde.KeyboardLayouts"), QStringLiteral("layoutListChanged"), this, SIGNAL(layoutListChanged())));
0047         QVERIFY(QDBusConnection::sessionBus().connect(QStringLiteral("org.kde.keyboard"), QStringLiteral("/Layouts"), QStringLiteral("org.kde.KeyboardLayouts"), QStringLiteral("layoutChanged"), this, SIGNAL(layoutChanged(uint))));
0048     }
0049 
0050 Q_SIGNALS:
0051     void layoutChanged(uint index);
0052     void layoutListChanged();
0053 
0054 private Q_SLOTS:
0055     void initTestCase();
0056     void init();
0057     void cleanup();
0058 
0059     void testReconfigure();
0060     void testChangeLayoutThroughDBus();
0061     void testPerLayoutShortcut();
0062     void testDBusServiceExport();
0063     void testVirtualDesktopPolicy();
0064     void testWindowPolicy();
0065     void testApplicationPolicy();
0066     void testNumLock();
0067 
0068 private:
0069     void reconfigureLayouts();
0070     void resetLayouts();
0071     auto changeLayout(uint index);
0072     void callSession(const QString &method);
0073     QSignalSpy layoutsReconfiguredSpy;
0074     QSignalSpy layoutChangedSpy;
0075     KConfigGroup layoutGroup;
0076 };
0077 
0078 void KeyboardLayoutTest::reconfigureLayouts()
0079 {
0080     // create DBus signal to reload
0081     QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/Layouts"), QStringLiteral("org.kde.keyboard"), QStringLiteral("reloadConfig"));
0082     QVERIFY(QDBusConnection::sessionBus().send(message));
0083 
0084     QVERIFY(layoutsReconfiguredSpy.wait(1000));
0085     QCOMPARE(layoutsReconfiguredSpy.count(), 1);
0086     layoutsReconfiguredSpy.clear();
0087 }
0088 
0089 void KeyboardLayoutTest::resetLayouts()
0090 {
0091     /* Switch Policy to destroy layouts from memory.
0092      * On return to original Policy they should reload from disk.
0093      */
0094     callSession(QStringLiteral("aboutToSaveSession"));
0095 
0096     const QString policy = layoutGroup.readEntry("SwitchMode", "Global");
0097 
0098     if (policy == QLatin1String("Global")) {
0099         layoutGroup.writeEntry("SwitchMode", "Desktop");
0100     } else {
0101         layoutGroup.deleteEntry("SwitchMode");
0102     }
0103     reconfigureLayouts();
0104 
0105     layoutGroup.writeEntry("SwitchMode", policy);
0106     reconfigureLayouts();
0107 
0108     callSession(QStringLiteral("loadSession"));
0109 }
0110 
0111 auto KeyboardLayoutTest::changeLayout(uint index)
0112 {
0113     QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.keyboard"),
0114                                                       QStringLiteral("/Layouts"),
0115                                                       QStringLiteral("org.kde.KeyboardLayouts"),
0116                                                       QStringLiteral("setLayout"));
0117     msg << index;
0118     return QDBusConnection::sessionBus().asyncCall(msg);
0119 }
0120 
0121 void KeyboardLayoutTest::callSession(const QString &method)
0122 {
0123     QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KWin"),
0124                                                       QStringLiteral("/Session"),
0125                                                       QStringLiteral("org.kde.KWin.Session"),
0126                                                       method);
0127     msg << QLatin1String(); // session name
0128     QVERIFY(QDBusConnection::sessionBus().call(msg).type() != QDBusMessage::ErrorMessage);
0129 }
0130 
0131 void KeyboardLayoutTest::initTestCase()
0132 {
0133     qRegisterMetaType<KWin::Window *>();
0134     QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
0135     QVERIFY(waylandServer()->init(s_socketName));
0136     QMetaObject::invokeMethod(kwinApp()->outputBackend(), "setVirtualOutputs", Qt::DirectConnection, Q_ARG(QVector<QRect>, QVector<QRect>() << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1280, 1024)));
0137 
0138     kwinApp()->setConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig));
0139     kwinApp()->setKxkbConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig));
0140 
0141     layoutGroup = kwinApp()->kxkbConfig()->group("Layout");
0142     layoutGroup.deleteGroup();
0143 
0144     kwinApp()->start();
0145     QVERIFY(applicationStartedSpy.wait());
0146 
0147     // don't get DBus signal on one-layout configuration
0148     //    QVERIFY(layoutsReconfiguredSpy.wait());
0149     //    QCOMPARE(layoutsReconfiguredSpy.count(), 1);
0150     //    layoutsReconfiguredSpy.clear();
0151 }
0152 
0153 void KeyboardLayoutTest::init()
0154 {
0155     QVERIFY(Test::setupWaylandConnection());
0156 }
0157 
0158 void KeyboardLayoutTest::cleanup()
0159 {
0160     Test::destroyWaylandConnection();
0161 }
0162 
0163 void KeyboardLayoutTest::testReconfigure()
0164 {
0165     // verifies that we can change the keymap
0166 
0167     // default should be a keymap with only us layout
0168     auto xkb = input()->keyboard()->xkb();
0169     QCOMPARE(xkb->numberOfLayouts(), 1u);
0170     QCOMPARE(xkb->layoutName(), QStringLiteral("English (US)"));
0171     QCOMPARE(xkb->numberOfLayouts(), 1);
0172     QCOMPARE(xkb->layoutName(0), QStringLiteral("English (US)"));
0173 
0174     // create a new keymap
0175     KConfigGroup layoutGroup = kwinApp()->kxkbConfig()->group("Layout");
0176     layoutGroup.writeEntry("LayoutList", QStringLiteral("de,us"));
0177     layoutGroup.sync();
0178 
0179     reconfigureLayouts();
0180     // now we should have two layouts
0181     QCOMPARE(xkb->numberOfLayouts(), 2u);
0182     // default layout is German
0183     QCOMPARE(xkb->layoutName(), QStringLiteral("German"));
0184     QCOMPARE(xkb->numberOfLayouts(), 2);
0185     QCOMPARE(xkb->layoutName(0), QStringLiteral("German"));
0186     QCOMPARE(xkb->layoutName(1), QStringLiteral("English (US)"));
0187 }
0188 
0189 void KeyboardLayoutTest::testChangeLayoutThroughDBus()
0190 {
0191     // this test verifies that the layout can be changed through DBus
0192     // first configure layouts
0193     enum Layout {
0194         de,
0195         us,
0196         de_neo,
0197         bad,
0198     };
0199     layoutGroup.writeEntry("LayoutList", QStringLiteral("de,us,de(neo)"));
0200     layoutGroup.sync();
0201     reconfigureLayouts();
0202     // now we should have three layouts
0203     auto xkb = input()->keyboard()->xkb();
0204     QCOMPARE(xkb->numberOfLayouts(), 3u);
0205     // default layout is German
0206     xkb->switchToLayout(0);
0207     QCOMPARE(xkb->layoutName(), QStringLiteral("German"));
0208 
0209     // place garbage to layout entry
0210     layoutGroup.writeEntry("LayoutDefaultFoo", "garbage");
0211     // make sure the garbage is wiped out on saving
0212     resetLayouts();
0213     QVERIFY(!layoutGroup.hasKey("LayoutDefaultFoo"));
0214 
0215     // now change through DBus to English
0216     auto reply = changeLayout(Layout::us);
0217     reply.waitForFinished();
0218     QVERIFY(!reply.isError());
0219     QCOMPARE(reply.reply().arguments().first().toBool(), true);
0220     QVERIFY(layoutChangedSpy.wait());
0221     QCOMPARE(layoutChangedSpy.count(), 1);
0222     layoutChangedSpy.clear();
0223 
0224     // layout should persist after reset
0225     resetLayouts();
0226     QCOMPARE(xkb->layoutName(), QStringLiteral("English (US)"));
0227     QVERIFY(layoutChangedSpy.wait());
0228     QCOMPARE(layoutChangedSpy.count(), 1);
0229     layoutChangedSpy.clear();
0230 
0231     // switch to a layout which does not exist
0232     reply = changeLayout(Layout::bad);
0233     QVERIFY(!reply.isError());
0234     QCOMPARE(reply.reply().arguments().first().toBool(), false);
0235     QCOMPARE(xkb->layoutName(), QStringLiteral("English (US)"));
0236     QVERIFY(!layoutChangedSpy.wait(1000));
0237 
0238     // switch to another layout should work
0239     reply = changeLayout(Layout::de);
0240     QVERIFY(!reply.isError());
0241     QCOMPARE(reply.reply().arguments().first().toBool(), true);
0242     QCOMPARE(xkb->layoutName(), QStringLiteral("German"));
0243     QVERIFY(layoutChangedSpy.wait(1000));
0244     QCOMPARE(layoutChangedSpy.count(), 1);
0245 
0246     // switching to same layout should also work
0247     reply = changeLayout(Layout::de);
0248     QVERIFY(!reply.isError());
0249     QCOMPARE(reply.reply().arguments().first().toBool(), true);
0250     QCOMPARE(xkb->layoutName(), QStringLiteral("German"));
0251     QVERIFY(!layoutChangedSpy.wait(1000));
0252 }
0253 
0254 void KeyboardLayoutTest::testPerLayoutShortcut()
0255 {
0256     // this test verifies that per-layout global shortcuts are working correctly.
0257     // first configure layouts
0258     layoutGroup.writeEntry("LayoutList", QStringLiteral("us,de,de(neo)"));
0259     layoutGroup.sync();
0260 
0261     // and create the global shortcuts
0262     const QString componentName = QStringLiteral("KDE Keyboard Layout Switcher");
0263     QAction *a = new QAction(this);
0264     a->setObjectName(QStringLiteral("Switch keyboard layout to English (US)"));
0265     a->setProperty("componentName", componentName);
0266     KGlobalAccel::self()->setShortcut(a, QList<QKeySequence>{Qt::CTRL | Qt::ALT | Qt::Key_1}, KGlobalAccel::NoAutoloading);
0267     delete a;
0268     a = new QAction(this);
0269     a->setObjectName(QStringLiteral("Switch keyboard layout to German"));
0270     a->setProperty("componentName", componentName);
0271     KGlobalAccel::self()->setShortcut(a, QList<QKeySequence>{Qt::CTRL | Qt::ALT | Qt::Key_2}, KGlobalAccel::NoAutoloading);
0272     delete a;
0273 
0274     // now we should have three layouts
0275     auto xkb = input()->keyboard()->xkb();
0276     reconfigureLayouts();
0277     QCOMPARE(xkb->numberOfLayouts(), 3u);
0278     // default layout is English
0279     xkb->switchToLayout(0);
0280     QCOMPARE(xkb->layoutName(), QStringLiteral("English (US)"));
0281 
0282     // now switch to English through the global shortcut
0283     quint32 timestamp = 1;
0284     Test::keyboardKeyPressed(KEY_LEFTCTRL, timestamp++);
0285     Test::keyboardKeyPressed(KEY_LEFTALT, timestamp++);
0286     Test::keyboardKeyPressed(KEY_2, timestamp++);
0287     QVERIFY(layoutChangedSpy.wait());
0288     // now layout should be German
0289     QCOMPARE(xkb->layoutName(), QStringLiteral("German"));
0290     // release keys again
0291     Test::keyboardKeyReleased(KEY_2, timestamp++);
0292     // switch back to English
0293     Test::keyboardKeyPressed(KEY_1, timestamp++);
0294     QVERIFY(layoutChangedSpy.wait());
0295     QCOMPARE(xkb->layoutName(), QStringLiteral("English (US)"));
0296     // release keys again
0297     Test::keyboardKeyReleased(KEY_1, timestamp++);
0298     Test::keyboardKeyReleased(KEY_LEFTALT, timestamp++);
0299     Test::keyboardKeyReleased(KEY_LEFTCTRL, timestamp++);
0300 }
0301 
0302 void KeyboardLayoutTest::testDBusServiceExport()
0303 {
0304     // verifies that the dbus service is only exported if there are at least two layouts
0305 
0306     // first configure layouts, with just one layout
0307     layoutGroup.writeEntry("LayoutList", QStringLiteral("us"));
0308     layoutGroup.sync();
0309     reconfigureLayouts();
0310     auto xkb = input()->keyboard()->xkb();
0311     QCOMPARE(xkb->numberOfLayouts(), 1u);
0312     // default layout is English
0313     QCOMPARE(xkb->layoutName(), QStringLiteral("English (US)"));
0314     // with one layout we should not have the dbus interface
0315     QVERIFY(!QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral("org.kde.keyboard")).value());
0316 
0317     // reconfigure to two layouts
0318     layoutGroup.writeEntry("LayoutList", QStringLiteral("us,de"));
0319     layoutGroup.sync();
0320     reconfigureLayouts();
0321     QCOMPARE(xkb->numberOfLayouts(), 2u);
0322     QVERIFY(QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral("org.kde.keyboard")).value());
0323 
0324     // and back to one layout
0325     layoutGroup.writeEntry("LayoutList", QStringLiteral("us"));
0326     layoutGroup.sync();
0327     reconfigureLayouts();
0328     QCOMPARE(xkb->numberOfLayouts(), 1u);
0329     QVERIFY(!QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral("org.kde.keyboard")).value());
0330 }
0331 
0332 void KeyboardLayoutTest::testVirtualDesktopPolicy()
0333 {
0334     layoutGroup.writeEntry("LayoutList", QStringLiteral("us,de,de(neo)"));
0335     layoutGroup.writeEntry("SwitchMode", QStringLiteral("Desktop"));
0336     layoutGroup.sync();
0337     reconfigureLayouts();
0338     auto xkb = input()->keyboard()->xkb();
0339     QCOMPARE(xkb->numberOfLayouts(), 3u);
0340     QCOMPARE(xkb->layoutName(), QStringLiteral("English (US)"));
0341 
0342     VirtualDesktopManager::self()->setCount(4);
0343     QCOMPARE(VirtualDesktopManager::self()->count(), 4u);
0344     auto desktops = VirtualDesktopManager::self()->desktops();
0345     QCOMPARE(desktops.count(), 4);
0346 
0347     // give desktops different layouts
0348     uint desktop, layout;
0349     for (desktop = 0; desktop < VirtualDesktopManager::self()->count(); ++desktop) {
0350         // switch to another virtual desktop
0351         VirtualDesktopManager::self()->setCurrent(desktops.at(desktop));
0352         QCOMPARE(desktops.at(desktop), VirtualDesktopManager::self()->currentDesktop());
0353         // should be reset to English
0354         QCOMPARE(xkb->currentLayout(), 0);
0355         // change first desktop to German
0356         layout = (desktop + 1) % xkb->numberOfLayouts();
0357         changeLayout(layout).waitForFinished();
0358         QCOMPARE(xkb->currentLayout(), layout);
0359     }
0360 
0361     // imitate app restart to test layouts saving feature
0362     resetLayouts();
0363 
0364     // check layout set on desktop switching as intended
0365     for (--desktop;;) {
0366         QCOMPARE(desktops.at(desktop), VirtualDesktopManager::self()->currentDesktop());
0367         layout = (desktop + 1) % xkb->numberOfLayouts();
0368         QCOMPARE(xkb->currentLayout(), layout);
0369         if (--desktop >= VirtualDesktopManager::self()->count()) { // overflow
0370             break;
0371         }
0372         VirtualDesktopManager::self()->setCurrent(desktops.at(desktop));
0373     }
0374 
0375     // remove virtual desktops
0376     desktop = 0;
0377     const KWin::VirtualDesktop *deletedDesktop = desktops.last();
0378     VirtualDesktopManager::self()->setCount(1);
0379     QCOMPARE(xkb->currentLayout(), layout = (desktop + 1) % xkb->numberOfLayouts());
0380     QCOMPARE(xkb->layoutName(), QStringLiteral("German"));
0381 
0382     // add another desktop
0383     VirtualDesktopManager::self()->setCount(2);
0384     // switching to it should result in going to default
0385     desktops = VirtualDesktopManager::self()->desktops();
0386     QCOMPARE(desktops.count(), 2);
0387     QCOMPARE(desktops.first(), VirtualDesktopManager::self()->currentDesktop());
0388     VirtualDesktopManager::self()->setCurrent(desktops.last());
0389     QCOMPARE(xkb->layoutName(), QStringLiteral("English (US)"));
0390 
0391     // check there are no more layouts left in config than the last actual non-default layouts number
0392     QSignalSpy deletedDesktopSpy(deletedDesktop, &VirtualDesktop::aboutToBeDestroyed);
0393     QVERIFY(deletedDesktopSpy.wait());
0394     resetLayouts();
0395     QCOMPARE(layoutGroup.keyList().filter(QStringLiteral("LayoutDefault")).count(), 1);
0396 }
0397 
0398 void KeyboardLayoutTest::testWindowPolicy()
0399 {
0400     enum Layout {
0401         us,
0402         de,
0403         de_neo,
0404         bad,
0405     };
0406     layoutGroup.writeEntry("LayoutList", QStringLiteral("us,de,de(neo)"));
0407     layoutGroup.writeEntry("SwitchMode", QStringLiteral("Window"));
0408     layoutGroup.sync();
0409     reconfigureLayouts();
0410     auto xkb = input()->keyboard()->xkb();
0411     QCOMPARE(xkb->numberOfLayouts(), 3u);
0412     QCOMPARE(xkb->layoutName(), QStringLiteral("English (US)"));
0413 
0414     // create a window
0415     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0416     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0417     auto c1 = Test::renderAndWaitForShown(surface.get(), QSize(100, 100), Qt::blue);
0418     QVERIFY(c1);
0419 
0420     // now switch layout
0421     auto reply = changeLayout(Layout::de);
0422     reply.waitForFinished();
0423     QCOMPARE(xkb->layoutName(), QStringLiteral("German"));
0424 
0425     // create a second window
0426     std::unique_ptr<KWayland::Client::Surface> surface2(Test::createSurface());
0427     std::unique_ptr<Test::XdgToplevel> shellSurface2(Test::createXdgToplevelSurface(surface2.get()));
0428     auto c2 = Test::renderAndWaitForShown(surface2.get(), QSize(100, 100), Qt::red);
0429     QVERIFY(c2);
0430     // this should have switched back to English
0431     QCOMPARE(xkb->layoutName(), QStringLiteral("English (US)"));
0432     // now change to another layout
0433     reply = changeLayout(Layout::de_neo);
0434     reply.waitForFinished();
0435     QCOMPARE(xkb->layoutName(), QStringLiteral("German (Neo 2)"));
0436 
0437     // activate other window
0438     workspace()->activateWindow(c1);
0439     QCOMPARE(xkb->layoutName(), QStringLiteral("German"));
0440     workspace()->activateWindow(c2);
0441     QCOMPARE(xkb->layoutName(), QStringLiteral("German (Neo 2)"));
0442 }
0443 
0444 void KeyboardLayoutTest::testApplicationPolicy()
0445 {
0446     enum Layout {
0447         us,
0448         de,
0449         de_neo,
0450         bad,
0451     };
0452     layoutGroup.writeEntry("LayoutList", QStringLiteral("us,de,de(neo)"));
0453     layoutGroup.writeEntry("SwitchMode", QStringLiteral("WinClass"));
0454     layoutGroup.sync();
0455     reconfigureLayouts();
0456     auto xkb = input()->keyboard()->xkb();
0457     QCOMPARE(xkb->numberOfLayouts(), 3u);
0458     QCOMPARE(xkb->layoutName(), QStringLiteral("English (US)"));
0459 
0460     // create a window
0461     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0462     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0463     shellSurface->set_app_id(QStringLiteral("org.kde.foo"));
0464     auto c1 = Test::renderAndWaitForShown(surface.get(), QSize(100, 100), Qt::blue);
0465     QVERIFY(c1);
0466 
0467     // create a second window
0468     std::unique_ptr<KWayland::Client::Surface> surface2(Test::createSurface());
0469     std::unique_ptr<Test::XdgToplevel> shellSurface2(Test::createXdgToplevelSurface(surface2.get()));
0470     shellSurface2->set_app_id(QStringLiteral("org.kde.foo"));
0471     auto c2 = Test::renderAndWaitForShown(surface2.get(), QSize(100, 100), Qt::red);
0472     QVERIFY(c2);
0473     // now switch layout
0474     layoutChangedSpy.clear();
0475     changeLayout(Layout::de_neo);
0476     QVERIFY(layoutChangedSpy.wait());
0477     QCOMPARE(layoutChangedSpy.count(), 1);
0478     layoutChangedSpy.clear();
0479     QCOMPARE(xkb->layoutName(), QStringLiteral("German (Neo 2)"));
0480 
0481     resetLayouts();
0482     // to trigger layout application for current client
0483     workspace()->activateWindow(c1);
0484     workspace()->activateWindow(c2);
0485     QVERIFY(layoutChangedSpy.wait());
0486     QCOMPARE(layoutChangedSpy.count(), 1);
0487     QCOMPARE(xkb->layoutName(), QStringLiteral("German (Neo 2)"));
0488 
0489     // activate other window
0490     workspace()->activateWindow(c1);
0491     // it is the same application and should not switch the layout
0492     QVERIFY(!layoutChangedSpy.wait(1000));
0493     QCOMPARE(xkb->layoutName(), QStringLiteral("German (Neo 2)"));
0494     workspace()->activateWindow(c2);
0495     QVERIFY(!layoutChangedSpy.wait(1000));
0496     QCOMPARE(xkb->layoutName(), QStringLiteral("German (Neo 2)"));
0497 
0498     shellSurface2.reset();
0499     surface2.reset();
0500     QVERIFY(Test::waitForWindowDestroyed(c2));
0501     QVERIFY(!layoutChangedSpy.wait(1000));
0502     QCOMPARE(xkb->layoutName(), QStringLiteral("German (Neo 2)"));
0503 
0504     resetLayouts();
0505     QCOMPARE(layoutGroup.keyList().filter(QStringLiteral("LayoutDefault")).count(), 1);
0506 }
0507 
0508 void KeyboardLayoutTest::testNumLock()
0509 {
0510     qputenv("KWIN_FORCE_NUM_LOCK_EVALUATION", "1");
0511     layoutGroup.writeEntry("LayoutList", QStringLiteral("us"));
0512     layoutGroup.sync();
0513     reconfigureLayouts();
0514 
0515     auto xkb = input()->keyboard()->xkb();
0516     QCOMPARE(xkb->numberOfLayouts(), 1u);
0517     QCOMPARE(xkb->layoutName(), QStringLiteral("English (US)"));
0518 
0519     // by default not set
0520     QVERIFY(!xkb->leds().testFlag(LED::NumLock));
0521     quint32 timestamp = 0;
0522     Test::keyboardKeyPressed(KEY_NUMLOCK, timestamp++);
0523     Test::keyboardKeyReleased(KEY_NUMLOCK, timestamp++);
0524     // now it should be on
0525     QVERIFY(xkb->leds().testFlag(LED::NumLock));
0526     // and back to off
0527     Test::keyboardKeyPressed(KEY_NUMLOCK, timestamp++);
0528     Test::keyboardKeyReleased(KEY_NUMLOCK, timestamp++);
0529     QVERIFY(!xkb->leds().testFlag(LED::NumLock));
0530 
0531     // let's reconfigure to enable through config
0532     auto group = InputConfig::self()->inputConfig()->group("Keyboard");
0533     group.writeEntry("NumLock", 0);
0534     group.sync();
0535     xkb->reconfigure();
0536     // now it should be on
0537     QVERIFY(xkb->leds().testFlag(LED::NumLock));
0538     // pressing should result in it being off
0539     Test::keyboardKeyPressed(KEY_NUMLOCK, timestamp++);
0540     Test::keyboardKeyReleased(KEY_NUMLOCK, timestamp++);
0541     QVERIFY(!xkb->leds().testFlag(LED::NumLock));
0542 
0543     // pressing again should enable it
0544     Test::keyboardKeyPressed(KEY_NUMLOCK, timestamp++);
0545     Test::keyboardKeyReleased(KEY_NUMLOCK, timestamp++);
0546     QVERIFY(xkb->leds().testFlag(LED::NumLock));
0547 
0548     // now reconfigure to disable on load
0549     group.writeEntry("NumLock", 1);
0550     group.sync();
0551     xkb->reconfigure();
0552     QVERIFY(!xkb->leds().testFlag(LED::NumLock));
0553 }
0554 
0555 WAYLANDTEST_MAIN(KeyboardLayoutTest)
0556 #include "keyboard_layout_test.moc"