File indexing completed on 2024-11-10 04:56:00
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"