File indexing completed on 2025-03-23 13:47:58
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"