File indexing completed on 2024-05-12 05:30:34

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