13#include "virtualdesktops.h"
19#include <KConfigGroup>
20#include <KGlobalAccel>
22#include <KWayland/Client/surface.h>
25#include <QDBusConnection>
26#include <QDBusConnectionInterface>
27#include <QDBusMessage>
28#include <QDBusPendingCall>
30#include <linux/input.h>
34static const QString s_socketName = QStringLiteral(
"wayland_test_kwin_keyboard_laout-0");
45 QVERIFY(QDBusConnection::sessionBus().connect(QStringLiteral(
"org.kde.keyboard"), QStringLiteral(
"/Layouts"), QStringLiteral(
"org.kde.KeyboardLayouts"), QStringLiteral(
"layoutListChanged"),
this, SIGNAL(
layoutListChanged())));
46 QVERIFY(QDBusConnection::sessionBus().connect(QStringLiteral(
"org.kde.keyboard"), QStringLiteral(
"/Layouts"), QStringLiteral(
"org.kde.KeyboardLayouts"), QStringLiteral(
"layoutChanged"),
this, SIGNAL(
layoutChanged(uint))));
58 void testReconfigure();
59 void testChangeLayoutThroughDBus();
60 void testPerLayoutShortcut();
61 void testDBusServiceExport();
62 void testVirtualDesktopPolicy();
63 void testWindowPolicy();
64 void testApplicationPolicy();
68 void reconfigureLayouts();
70 auto changeLayout(uint index);
71 void callSession(
const QString &method);
72 QSignalSpy layoutsReconfiguredSpy;
73 QSignalSpy layoutChangedSpy;
74 KConfigGroup layoutGroup;
77void KeyboardLayoutTest::reconfigureLayouts()
80 QDBusMessage message = QDBusMessage::createSignal(QStringLiteral(
"/Layouts"), QStringLiteral(
"org.kde.keyboard"), QStringLiteral(
"reloadConfig"));
81 QVERIFY(QDBusConnection::sessionBus().send(message));
83 QVERIFY(layoutsReconfiguredSpy.wait(1000));
84 QCOMPARE(layoutsReconfiguredSpy.count(), 1);
85 layoutsReconfiguredSpy.clear();
88void KeyboardLayoutTest::resetLayouts()
93 callSession(QStringLiteral(
"aboutToSaveSession"));
95 const QString policy = layoutGroup.readEntry(
"SwitchMode",
"Global");
97 if (policy == QLatin1String(
"Global")) {
98 layoutGroup.writeEntry(
"SwitchMode",
"Desktop");
100 layoutGroup.deleteEntry(
"SwitchMode");
102 reconfigureLayouts();
104 layoutGroup.writeEntry(
"SwitchMode", policy);
105 reconfigureLayouts();
107 callSession(QStringLiteral(
"loadSession"));
110auto KeyboardLayoutTest::changeLayout(uint index)
112 QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral(
"org.kde.keyboard"),
113 QStringLiteral(
"/Layouts"),
114 QStringLiteral(
"org.kde.KeyboardLayouts"),
115 QStringLiteral(
"setLayout"));
117 return QDBusConnection::sessionBus().asyncCall(msg);
120void KeyboardLayoutTest::callSession(
const QString &method)
122 QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral(
"org.kde.KWin"),
123 QStringLiteral(
"/Session"),
124 QStringLiteral(
"org.kde.KWin.Session"),
126 msg << QLatin1String();
127 QVERIFY(QDBusConnection::sessionBus().call(msg).
type() != QDBusMessage::ErrorMessage);
130void KeyboardLayoutTest::initTestCase()
132 qRegisterMetaType<KWin::Window *>();
136 QRect(0, 0, 1280, 1024),
137 QRect(1280, 0, 1280, 1024),
140 kwinApp()->setConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig));
141 kwinApp()->setKxkbConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig));
142 kwinApp()->setInputConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig));
144 layoutGroup = kwinApp()->kxkbConfig()->group(QStringLiteral(
"Layout"));
145 layoutGroup.deleteGroup();
148 QVERIFY(applicationStartedSpy.wait());
156void KeyboardLayoutTest::init()
161void KeyboardLayoutTest::cleanup()
166void KeyboardLayoutTest::testReconfigure()
172 QCOMPARE(xkb->numberOfLayouts(), 1u);
173 QCOMPARE(xkb->layoutName(), QStringLiteral(
"English (US)"));
174 QCOMPARE(xkb->numberOfLayouts(), 1);
175 QCOMPARE(xkb->layoutName(0), QStringLiteral(
"English (US)"));
178 KConfigGroup layoutGroup = kwinApp()->kxkbConfig()->group(QStringLiteral(
"Layout"));
179 layoutGroup.writeEntry(
"LayoutList", QStringLiteral(
"de,us"));
182 reconfigureLayouts();
184 QCOMPARE(xkb->numberOfLayouts(), 2u);
186 QCOMPARE(xkb->layoutName(), QStringLiteral(
"German"));
187 QCOMPARE(xkb->numberOfLayouts(), 2);
188 QCOMPARE(xkb->layoutName(0), QStringLiteral(
"German"));
189 QCOMPARE(xkb->layoutName(1), QStringLiteral(
"English (US)"));
192void KeyboardLayoutTest::testChangeLayoutThroughDBus()
202 layoutGroup.writeEntry(
"LayoutList", QStringLiteral(
"de,us,de(neo)"));
204 reconfigureLayouts();
207 QCOMPARE(xkb->numberOfLayouts(), 3u);
209 xkb->switchToLayout(0);
210 QCOMPARE(xkb->layoutName(), QStringLiteral(
"German"));
213 layoutGroup.writeEntry(
"LayoutDefaultFoo",
"garbage");
216 QVERIFY(!layoutGroup.hasKey(
"LayoutDefaultFoo"));
219 auto reply = changeLayout(Layout::us);
220 reply.waitForFinished();
221 QVERIFY(!reply.isError());
222 QCOMPARE(reply.reply().arguments().first().toBool(),
true);
223 QVERIFY(layoutChangedSpy.wait());
224 QCOMPARE(layoutChangedSpy.count(), 1);
225 layoutChangedSpy.clear();
229 QCOMPARE(xkb->layoutName(), QStringLiteral(
"English (US)"));
230 QVERIFY(layoutChangedSpy.wait());
231 QCOMPARE(layoutChangedSpy.count(), 1);
232 layoutChangedSpy.clear();
235 reply = changeLayout(Layout::bad);
236 QVERIFY(!reply.isError());
237 QCOMPARE(reply.reply().arguments().first().toBool(),
false);
238 QCOMPARE(xkb->layoutName(), QStringLiteral(
"English (US)"));
239 QVERIFY(!layoutChangedSpy.wait(1000));
242 reply = changeLayout(Layout::de);
243 QVERIFY(!reply.isError());
244 QCOMPARE(reply.reply().arguments().first().toBool(),
true);
245 QCOMPARE(xkb->layoutName(), QStringLiteral(
"German"));
246 QVERIFY(layoutChangedSpy.wait(1000));
247 QCOMPARE(layoutChangedSpy.count(), 1);
250 reply = changeLayout(Layout::de);
251 QVERIFY(!reply.isError());
252 QCOMPARE(reply.reply().arguments().first().toBool(),
true);
253 QCOMPARE(xkb->layoutName(), QStringLiteral(
"German"));
254 QVERIFY(!layoutChangedSpy.wait(1000));
257void KeyboardLayoutTest::testPerLayoutShortcut()
259#if !KWIN_BUILD_GLOBALSHORTCUTS
260 QSKIP(
"Can't test shortcuts without shortcuts");
266 layoutGroup.writeEntry(
"LayoutList", QStringLiteral(
"us,de,de(neo)"));
270 const QString componentName = QStringLiteral(
"KDE Keyboard Layout Switcher");
271 QAction *a =
new QAction(
this);
272 a->setObjectName(QStringLiteral(
"Switch keyboard layout to English (US)"));
273 a->setProperty(
"componentName", componentName);
274 KGlobalAccel::self()->setShortcut(a, QList<QKeySequence>{Qt::CTRL | Qt::ALT | Qt::Key_1}, KGlobalAccel::NoAutoloading);
276 a =
new QAction(
this);
277 a->setObjectName(QStringLiteral(
"Switch keyboard layout to German"));
278 a->setProperty(
"componentName", componentName);
279 KGlobalAccel::self()->setShortcut(a, QList<QKeySequence>{Qt::CTRL | Qt::ALT | Qt::Key_2}, KGlobalAccel::NoAutoloading);
284 reconfigureLayouts();
285 QCOMPARE(xkb->numberOfLayouts(), 3u);
287 xkb->switchToLayout(0);
288 QCOMPARE(xkb->layoutName(), QStringLiteral(
"English (US)"));
291 quint32 timestamp = 1;
295 QVERIFY(layoutChangedSpy.wait());
297 QCOMPARE(xkb->layoutName(), QStringLiteral(
"German"));
302 QVERIFY(layoutChangedSpy.wait());
303 QCOMPARE(xkb->layoutName(), QStringLiteral(
"English (US)"));
310void KeyboardLayoutTest::testDBusServiceExport()
315 layoutGroup.writeEntry(
"LayoutList", QStringLiteral(
"us"));
317 reconfigureLayouts();
319 QCOMPARE(xkb->numberOfLayouts(), 1u);
321 QCOMPARE(xkb->layoutName(), QStringLiteral(
"English (US)"));
323 QVERIFY(!QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral(
"org.kde.keyboard")).value());
326 layoutGroup.writeEntry(
"LayoutList", QStringLiteral(
"us,de"));
328 reconfigureLayouts();
329 QCOMPARE(xkb->numberOfLayouts(), 2u);
330 QVERIFY(QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral(
"org.kde.keyboard")).value());
333 layoutGroup.writeEntry(
"LayoutList", QStringLiteral(
"us"));
335 reconfigureLayouts();
336 QCOMPARE(xkb->numberOfLayouts(), 1u);
337 QVERIFY(!QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral(
"org.kde.keyboard")).value());
340void KeyboardLayoutTest::testVirtualDesktopPolicy()
342 layoutGroup.writeEntry(
"LayoutList", QStringLiteral(
"us,de,de(neo)"));
343 layoutGroup.writeEntry(
"SwitchMode", QStringLiteral(
"Desktop"));
345 reconfigureLayouts();
347 QCOMPARE(xkb->numberOfLayouts(), 3u);
348 QCOMPARE(xkb->layoutName(), QStringLiteral(
"English (US)"));
350 VirtualDesktopManager::self()->setCount(4);
351 QCOMPARE(VirtualDesktopManager::self()->count(), 4u);
352 auto desktops = VirtualDesktopManager::self()->desktops();
353 QCOMPARE(desktops.count(), 4);
356 uint desktop, layout;
357 for (desktop = 0; desktop < VirtualDesktopManager::self()->count(); ++desktop) {
359 VirtualDesktopManager::self()->setCurrent(desktops.at(desktop));
360 QCOMPARE(desktops.at(desktop), VirtualDesktopManager::self()->currentDesktop());
362 QCOMPARE(xkb->currentLayout(), 0);
364 layout = (desktop + 1) % xkb->numberOfLayouts();
365 changeLayout(layout).waitForFinished();
366 QCOMPARE(xkb->currentLayout(), layout);
374 QCOMPARE(desktops.at(desktop), VirtualDesktopManager::self()->currentDesktop());
375 layout = (desktop + 1) % xkb->numberOfLayouts();
376 QCOMPARE(xkb->currentLayout(), layout);
377 if (--desktop >= VirtualDesktopManager::self()->count()) {
380 VirtualDesktopManager::self()->setCurrent(desktops.at(desktop));
386 VirtualDesktopManager::self()->setCount(1);
387 QCOMPARE(xkb->currentLayout(), layout = (desktop + 1) % xkb->numberOfLayouts());
388 QCOMPARE(xkb->layoutName(), QStringLiteral(
"German"));
391 VirtualDesktopManager::self()->setCount(2);
393 desktops = VirtualDesktopManager::self()->desktops();
394 QCOMPARE(desktops.count(), 2);
395 QCOMPARE(desktops.first(), VirtualDesktopManager::self()->currentDesktop());
396 VirtualDesktopManager::self()->setCurrent(desktops.last());
397 QCOMPARE(xkb->layoutName(), QStringLiteral(
"English (US)"));
401 QVERIFY(deletedDesktopSpy.wait());
403 QCOMPARE(layoutGroup.keyList().filter(QStringLiteral(
"LayoutDefault")).count(), 1);
406void KeyboardLayoutTest::testWindowPolicy()
414 layoutGroup.writeEntry(
"LayoutList", QStringLiteral(
"us,de,de(neo)"));
415 layoutGroup.writeEntry(
"SwitchMode", QStringLiteral(
"Window"));
417 reconfigureLayouts();
419 QCOMPARE(xkb->numberOfLayouts(), 3u);
420 QCOMPARE(xkb->layoutName(), QStringLiteral(
"English (US)"));
429 auto reply = changeLayout(Layout::de);
430 reply.waitForFinished();
431 QCOMPARE(xkb->layoutName(), QStringLiteral(
"German"));
439 QCOMPARE(xkb->layoutName(), QStringLiteral(
"English (US)"));
441 reply = changeLayout(Layout::de_neo);
442 reply.waitForFinished();
443 QCOMPARE(xkb->layoutName(), QStringLiteral(
"German (Neo 2)"));
447 QCOMPARE(xkb->layoutName(), QStringLiteral(
"German"));
449 QCOMPARE(xkb->layoutName(), QStringLiteral(
"German (Neo 2)"));
452void KeyboardLayoutTest::testApplicationPolicy()
460 layoutGroup.writeEntry(
"LayoutList", QStringLiteral(
"us,de,de(neo)"));
461 layoutGroup.writeEntry(
"SwitchMode", QStringLiteral(
"WinClass"));
463 reconfigureLayouts();
465 QCOMPARE(xkb->numberOfLayouts(), 3u);
466 QCOMPARE(xkb->layoutName(), QStringLiteral(
"English (US)"));
471 shellSurface->set_app_id(QStringLiteral(
"org.kde.foo"));
478 shellSurface2->set_app_id(QStringLiteral(
"org.kde.foo"));
482 layoutChangedSpy.clear();
483 changeLayout(Layout::de_neo);
484 QVERIFY(layoutChangedSpy.wait());
485 QCOMPARE(layoutChangedSpy.count(), 1);
486 layoutChangedSpy.clear();
487 QCOMPARE(xkb->layoutName(), QStringLiteral(
"German (Neo 2)"));
493 QVERIFY(layoutChangedSpy.wait());
494 QCOMPARE(layoutChangedSpy.count(), 1);
495 QCOMPARE(xkb->layoutName(), QStringLiteral(
"German (Neo 2)"));
500 QVERIFY(!layoutChangedSpy.wait(1000));
501 QCOMPARE(xkb->layoutName(), QStringLiteral(
"German (Neo 2)"));
503 QVERIFY(!layoutChangedSpy.wait(1000));
504 QCOMPARE(xkb->layoutName(), QStringLiteral(
"German (Neo 2)"));
506 shellSurface2.reset();
509 QVERIFY(!layoutChangedSpy.wait(1000));
510 QCOMPARE(xkb->layoutName(), QStringLiteral(
"German (Neo 2)"));
513 QCOMPARE(layoutGroup.keyList().filter(QStringLiteral(
"LayoutDefault")).count(), 1);
516void KeyboardLayoutTest::testNumLock()
518 qputenv(
"KWIN_FORCE_NUM_LOCK_EVALUATION",
"1");
519 layoutGroup.writeEntry(
"LayoutList", QStringLiteral(
"us"));
521 reconfigureLayouts();
524 QCOMPARE(xkb->numberOfLayouts(), 1u);
525 QCOMPARE(xkb->layoutName(), QStringLiteral(
"English (US)"));
528 QVERIFY(!xkb->leds().testFlag(LED::NumLock));
529 quint32 timestamp = 0;
533 QVERIFY(xkb->leds().testFlag(LED::NumLock));
537 QVERIFY(!xkb->leds().testFlag(LED::NumLock));
540 auto group = kwinApp()->inputConfig()->group(QStringLiteral(
"Keyboard"));
541 group.writeEntry(
"NumLock", 0);
545 QVERIFY(xkb->leds().testFlag(LED::NumLock));
549 QVERIFY(!xkb->leds().testFlag(LED::NumLock));
554 QVERIFY(xkb->leds().testFlag(LED::NumLock));
557 group.writeEntry(
"NumLock", 1);
560 QVERIFY(!xkb->leds().testFlag(LED::NumLock));
564#include "keyboard_layout_test.moc"
void aboutToBeDestroyed()
void activateWindow(Window *window, bool force=false)
void layoutChanged(uint index)
#define WAYLANDTEST_MAIN(TestObject)
Window * renderAndWaitForShown(KWayland::Client::Surface *surface, const QSize &size, const QColor &color, const QImage::Format &format=QImage::Format_ARGB32, int timeout=5000)
void keyboardKeyReleased(quint32 key, quint32 time)
void destroyWaylandConnection()
void setOutputConfig(const QList< QRect > &geometries)
void keyboardKeyPressed(quint32 key, quint32 time)
bool setupWaylandConnection(AdditionalWaylandInterfaces flags=AdditionalWaylandInterfaces())
std::unique_ptr< KWayland::Client::Surface > createSurface()
XdgToplevel * createXdgToplevelSurface(KWayland::Client::Surface *surface, QObject *parent=nullptr)
bool waitForWindowClosed(Window *window)
WaylandServer * waylandServer()
InputRedirection * input()