Introduce support for keyboard layout switching policies
Summary: This change introduces the initial support for keyboard layout switching policies like in the X11 session. This first change only adds support for Global and Virtual Desktop policy. This means the current layout is stored in context to the current virtual desktop. Whenever one changes the virtual desktop the previous layout is restored. If the user has not yet navigated to this virtual desktop a switch to default layout is performed. This is the first code interacting with the new Virtual Desktop API which is not based on integer ids. To fully support this the API is slightly extended. Test Plan: Added test case Reviewers: #kwin, #plasma Subscribers: plasma-devel, kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D5301
This commit is contained in:
parent
a5735e19b9
commit
bf99d9ffdd
8 changed files with 314 additions and 3 deletions
|
@ -367,6 +367,7 @@ set(kwin_KDEINIT_SRCS
|
|||
input_event_spy.cpp
|
||||
keyboard_input.cpp
|
||||
keyboard_layout.cpp
|
||||
keyboard_layout_switching.cpp
|
||||
keyboard_repeat.cpp
|
||||
pointer_input.cpp
|
||||
touch_input.cpp
|
||||
|
|
|
@ -21,6 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#include "keyboard_input.h"
|
||||
#include "keyboard_layout.h"
|
||||
#include "platform.h"
|
||||
#include "virtualdesktops.h"
|
||||
#include "wayland_server.h"
|
||||
|
||||
#include <KConfigGroup>
|
||||
|
@ -51,6 +52,7 @@ private Q_SLOTS:
|
|||
void testChangeLayoutThroughDBus();
|
||||
void testPerLayoutShortcut();
|
||||
void testDBusServiceExport();
|
||||
void testVirtualDesktopPolicy();
|
||||
|
||||
private:
|
||||
void reconfigureLayouts();
|
||||
|
@ -275,5 +277,65 @@ void KeyboardLayoutTest::testDBusServiceExport()
|
|||
QTRY_VERIFY(!QDBusConnection::sessionBus().interface()->isServiceRegistered(QStringLiteral("org.kde.keyboard")).value());
|
||||
}
|
||||
|
||||
void KeyboardLayoutTest::testVirtualDesktopPolicy()
|
||||
{
|
||||
KConfigGroup layoutGroup = kwinApp()->kxkbConfig()->group("Layout");
|
||||
layoutGroup.writeEntry("LayoutList", QStringLiteral("us,de,de(neo)"));
|
||||
layoutGroup.writeEntry("SwitchMode", QStringLiteral("Desktop"));
|
||||
layoutGroup.sync();
|
||||
reconfigureLayouts();
|
||||
auto xkb = input()->keyboard()->xkb();
|
||||
QTRY_COMPARE(xkb->numberOfLayouts(), 3u);
|
||||
QTRY_COMPARE(xkb->layoutName(), QStringLiteral("English (US)"));
|
||||
|
||||
VirtualDesktopManager::self()->setCount(4);
|
||||
QCOMPARE(VirtualDesktopManager::self()->count(), 4u);
|
||||
|
||||
auto changeLayout = [] (const QString &layoutName) {
|
||||
QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.keyboard"), QStringLiteral("/Layouts"), QStringLiteral("org.kde.KeyboardLayouts"), QStringLiteral("setLayout"));
|
||||
msg << layoutName;
|
||||
return QDBusConnection::sessionBus().asyncCall(msg);
|
||||
};
|
||||
auto reply = changeLayout(QStringLiteral("German"));
|
||||
reply.waitForFinished();
|
||||
QTRY_COMPARE(xkb->layoutName(), QStringLiteral("German"));
|
||||
|
||||
// switch to another virtual desktop
|
||||
auto desktops = VirtualDesktopManager::self()->desktops();
|
||||
QCOMPARE(desktops.count(), 4);
|
||||
QCOMPARE(desktops.first(), VirtualDesktopManager::self()->currentDesktop());
|
||||
VirtualDesktopManager::self()->setCurrent(desktops.at(1));
|
||||
QCOMPARE(desktops.at(1), VirtualDesktopManager::self()->currentDesktop());
|
||||
// should be reset to English
|
||||
QTRY_COMPARE(xkb->layoutName(), QStringLiteral("English (US)"));reply = changeLayout(QStringLiteral("German (Neo 2)"));
|
||||
reply.waitForFinished();
|
||||
QTRY_COMPARE(xkb->layoutName(), QStringLiteral("German (Neo 2)"));
|
||||
|
||||
// back to desktop 0 -> German
|
||||
VirtualDesktopManager::self()->setCurrent(desktops.at(0));
|
||||
QCOMPARE(desktops.first(), VirtualDesktopManager::self()->currentDesktop());
|
||||
QTRY_COMPARE(xkb->layoutName(), QStringLiteral("German"));
|
||||
// desktop 2 -> English
|
||||
VirtualDesktopManager::self()->setCurrent(desktops.at(2));
|
||||
QTRY_COMPARE(xkb->layoutName(), QStringLiteral("English (US)"));
|
||||
// desktop 1 -> Neo
|
||||
VirtualDesktopManager::self()->setCurrent(desktops.at(1));
|
||||
QTRY_COMPARE(xkb->layoutName(), QStringLiteral("German (Neo 2)"));
|
||||
|
||||
// remove virtual desktops
|
||||
VirtualDesktopManager::self()->setCount(1);
|
||||
QTRY_COMPARE(xkb->layoutName(), QStringLiteral("German"));
|
||||
|
||||
// add another desktop
|
||||
VirtualDesktopManager::self()->setCount(2);
|
||||
// switching to it should result in going to default
|
||||
desktops = VirtualDesktopManager::self()->desktops();
|
||||
QCOMPARE(desktops.count(), 2);
|
||||
QCOMPARE(desktops.first(), VirtualDesktopManager::self()->currentDesktop());
|
||||
VirtualDesktopManager::self()->setCurrent(desktops.last());
|
||||
QTRY_COMPARE(xkb->layoutName(), QStringLiteral("English (US)"));
|
||||
|
||||
}
|
||||
|
||||
WAYLANDTEST_MAIN(KeyboardLayoutTest)
|
||||
#include "keyboard_layout_test.moc"
|
||||
|
|
|
@ -18,6 +18,7 @@ You should have received a copy of the GNU General Public License
|
|||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#include "keyboard_layout.h"
|
||||
#include "keyboard_layout_switching.h"
|
||||
#include "keyboard_input.h"
|
||||
#include "input_event.h"
|
||||
#include "main.h"
|
||||
|
@ -162,6 +163,11 @@ void KeyboardLayout::reconfigure()
|
|||
{
|
||||
if (m_config) {
|
||||
m_config->reparseConfiguration();
|
||||
const QString policyKey = m_config->group(QStringLiteral("Layout")).readEntry("SwitchMode", QStringLiteral("Global"));
|
||||
if (!m_policy || m_policy->name() != policyKey) {
|
||||
delete m_policy;
|
||||
m_policy = KeyboardLayoutSwitching::Policy::create(m_xkb, this, policyKey);
|
||||
}
|
||||
}
|
||||
m_xkb->reconfigure();
|
||||
resetLayout();
|
||||
|
|
|
@ -35,6 +35,11 @@ namespace KWin
|
|||
class Xkb;
|
||||
class KeyboardLayoutDBusInterface;
|
||||
|
||||
namespace KeyboardLayoutSwitching
|
||||
{
|
||||
class Policy;
|
||||
}
|
||||
|
||||
class KeyboardLayout : public QObject, public InputEventSpy
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -76,6 +81,7 @@ private:
|
|||
KSharedConfigPtr m_config;
|
||||
QVector<QAction*> m_layoutShortcuts;
|
||||
KeyboardLayoutDBusInterface *m_dbusInterface = nullptr;
|
||||
KeyboardLayoutSwitching::Policy *m_policy = nullptr;
|
||||
};
|
||||
|
||||
class KeyboardLayoutDBusInterface : public QObject
|
||||
|
|
119
keyboard_layout_switching.cpp
Normal file
119
keyboard_layout_switching.cpp
Normal file
|
@ -0,0 +1,119 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2017 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#include "keyboard_layout_switching.h"
|
||||
#include "keyboard_layout.h"
|
||||
#include "virtualdesktops.h"
|
||||
#include "xkb.h"
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
namespace KeyboardLayoutSwitching
|
||||
{
|
||||
|
||||
Policy::Policy(Xkb *xkb, KeyboardLayout *layout)
|
||||
: QObject(layout)
|
||||
, m_xkb(xkb)
|
||||
, m_layout(layout)
|
||||
{
|
||||
connect(m_layout, &KeyboardLayout::layoutsReconfigured, this, &Policy::clearCache);
|
||||
connect(m_layout, &KeyboardLayout::layoutChanged, this, &Policy::layoutChanged);
|
||||
}
|
||||
|
||||
Policy::~Policy() = default;
|
||||
|
||||
void Policy::setLayout(quint32 layout)
|
||||
{
|
||||
m_xkb->switchToLayout(layout);
|
||||
}
|
||||
|
||||
quint32 Policy::layout() const
|
||||
{
|
||||
return m_xkb->currentLayout();
|
||||
}
|
||||
|
||||
Policy *Policy::create(Xkb *xkb, KeyboardLayout *layout, const QString &policy)
|
||||
{
|
||||
if (policy.toLower() == QStringLiteral("desktop")) {
|
||||
return new VirtualDesktopPolicy(xkb, layout);
|
||||
}
|
||||
return new GlobalPolicy(xkb, layout);
|
||||
}
|
||||
|
||||
GlobalPolicy::GlobalPolicy(Xkb *xkb, KeyboardLayout *layout)
|
||||
: Policy(xkb, layout)
|
||||
{
|
||||
}
|
||||
|
||||
GlobalPolicy::~GlobalPolicy() = default;
|
||||
|
||||
VirtualDesktopPolicy::VirtualDesktopPolicy(Xkb *xkb, KeyboardLayout *layout)
|
||||
: Policy(xkb, layout)
|
||||
{
|
||||
connect(VirtualDesktopManager::self(), &VirtualDesktopManager::currentChanged, this, &VirtualDesktopPolicy::desktopChanged);
|
||||
}
|
||||
|
||||
VirtualDesktopPolicy::~VirtualDesktopPolicy() = default;
|
||||
|
||||
void VirtualDesktopPolicy::clearCache()
|
||||
{
|
||||
m_layouts.clear();
|
||||
}
|
||||
|
||||
void VirtualDesktopPolicy::desktopChanged()
|
||||
{
|
||||
auto d = VirtualDesktopManager::self()->currentDesktop();
|
||||
if (!d) {
|
||||
return;
|
||||
}
|
||||
auto it = m_layouts.constFind(d);
|
||||
if (it == m_layouts.constEnd()) {
|
||||
// new desktop - go to default;
|
||||
setLayout(0);
|
||||
} else {
|
||||
setLayout(it.value());
|
||||
}
|
||||
}
|
||||
|
||||
void VirtualDesktopPolicy::layoutChanged()
|
||||
{
|
||||
auto d = VirtualDesktopManager::self()->currentDesktop();
|
||||
if (!d) {
|
||||
return;
|
||||
}
|
||||
auto it = m_layouts.find(d);
|
||||
const auto l = layout();
|
||||
if (it == m_layouts.constEnd()) {
|
||||
m_layouts.insert(d, l);
|
||||
connect(d, &VirtualDesktop::aboutToBeDestroyed, this,
|
||||
[this, d] {
|
||||
m_layouts.remove(d);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
if (it.value() == l) {
|
||||
return;
|
||||
}
|
||||
it.value() = l;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
98
keyboard_layout_switching.h
Normal file
98
keyboard_layout_switching.h
Normal file
|
@ -0,0 +1,98 @@
|
|||
/********************************************************************
|
||||
KWin - the KDE window manager
|
||||
This file is part of the KDE project.
|
||||
|
||||
Copyright (C) 2017 Martin Gräßlin <mgraesslin@kde.org>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*********************************************************************/
|
||||
#ifndef KWIN_KEYBOARD_LAYOUT_SWITCHING_H
|
||||
#define KWIN_KEYBOARD_LAYOUT_SWITCHING_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QHash>
|
||||
|
||||
namespace KWin
|
||||
{
|
||||
|
||||
class KeyboardLayout;
|
||||
class Xkb;
|
||||
class VirtualDesktop;
|
||||
|
||||
namespace KeyboardLayoutSwitching
|
||||
{
|
||||
|
||||
class Policy : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
virtual ~Policy();
|
||||
|
||||
virtual QString name() const = 0;
|
||||
|
||||
static Policy *create(Xkb *xkb, KeyboardLayout *layout, const QString &policy);
|
||||
|
||||
protected:
|
||||
explicit Policy(Xkb *xkb, KeyboardLayout *layout);
|
||||
virtual void clearCache() = 0;
|
||||
virtual void layoutChanged() = 0;
|
||||
|
||||
void setLayout(quint32 layout);
|
||||
quint32 layout() const;
|
||||
|
||||
private:
|
||||
Xkb *m_xkb;
|
||||
KeyboardLayout *m_layout;
|
||||
};
|
||||
|
||||
class GlobalPolicy : public Policy
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit GlobalPolicy(Xkb *xkb, KeyboardLayout *layout);
|
||||
~GlobalPolicy() override;
|
||||
|
||||
QString name() const override {
|
||||
return QStringLiteral("Global");
|
||||
}
|
||||
|
||||
protected:
|
||||
void clearCache() override {}
|
||||
void layoutChanged() override {}
|
||||
};
|
||||
|
||||
class VirtualDesktopPolicy : public Policy
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit VirtualDesktopPolicy(Xkb *xkb, KeyboardLayout *layout);
|
||||
~VirtualDesktopPolicy() override;
|
||||
|
||||
QString name() const override {
|
||||
return QStringLiteral("Desktop");
|
||||
}
|
||||
|
||||
protected:
|
||||
void clearCache() override;
|
||||
void layoutChanged() override;
|
||||
|
||||
private:
|
||||
void desktopChanged();
|
||||
QHash<VirtualDesktop *, quint32> m_layouts;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -39,7 +39,10 @@ VirtualDesktop::VirtualDesktop(QObject *parent)
|
|||
{
|
||||
}
|
||||
|
||||
VirtualDesktop::~VirtualDesktop() = default;
|
||||
VirtualDesktop::~VirtualDesktop()
|
||||
{
|
||||
emit aboutToBeDestroyed();
|
||||
}
|
||||
|
||||
void VirtualDesktop::setId(const QByteArray &id)
|
||||
{
|
||||
|
@ -327,6 +330,11 @@ uint VirtualDesktopManager::current() const
|
|||
return m_current ? m_current->x11DesktopNumber() : 0;
|
||||
}
|
||||
|
||||
VirtualDesktop *VirtualDesktopManager::currentDesktop() const
|
||||
{
|
||||
return m_current;
|
||||
}
|
||||
|
||||
bool VirtualDesktopManager::setCurrent(uint newDesktop)
|
||||
{
|
||||
if (newDesktop < 1 || newDesktop > count() || newDesktop == current()) {
|
||||
|
|
|
@ -21,6 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#define KWIN_VIRTUAL_DESKTOPS_H
|
||||
// KWin
|
||||
#include <kwinglobals.h>
|
||||
#include <kwin_export.h>
|
||||
// Qt includes
|
||||
#include <QObject>
|
||||
#include <QPoint>
|
||||
|
@ -36,7 +37,7 @@ class QAction;
|
|||
|
||||
namespace KWin {
|
||||
|
||||
class VirtualDesktop : public QObject
|
||||
class KWIN_EXPORT VirtualDesktop : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(QByteArray id READ id CONSTANT)
|
||||
|
@ -63,6 +64,10 @@ public:
|
|||
|
||||
Q_SIGNALS:
|
||||
void nameChanged();
|
||||
/**
|
||||
* Emitted just before the desktop gets destroyed.
|
||||
**/
|
||||
void aboutToBeDestroyed();
|
||||
|
||||
private:
|
||||
QByteArray m_id;
|
||||
|
@ -124,7 +129,7 @@ private:
|
|||
* of an adjacent desktop or to switch to an adjacent desktop. Interested parties should make use of
|
||||
* these methods and not replicate the logic to switch to the next desktop.
|
||||
**/
|
||||
class VirtualDesktopManager : public QObject
|
||||
class KWIN_EXPORT VirtualDesktopManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
/**
|
||||
|
@ -162,6 +167,12 @@ public:
|
|||
* @see currentChanged
|
||||
*/
|
||||
uint current() const;
|
||||
/**
|
||||
* @returns The current desktop
|
||||
* @see setCurrent
|
||||
* @see currentChanged
|
||||
**/
|
||||
VirtualDesktop *currentDesktop() const;
|
||||
/**
|
||||
* Moves to the desktop through the algorithm described by Direction.
|
||||
* @param wrap If @c true wraps around to the other side of the layout
|
||||
|
|
Loading…
Reference in a new issue