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()