KWin
Loading...
Searching...
No Matches
keyboard_layout.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2016, 2017 Martin Gräßlin <mgraesslin@kde.org>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9#include "keyboard_layout.h"
10#include "input_event.h"
11#include "keyboard_input.h"
13#include "xkb.h"
14
15#include <KGlobalAccel>
16#include <KLocalizedString>
17#include <QAction>
18#include <QDBusConnection>
19#include <QDBusMessage>
20#include <QDBusMetaType>
21#include <QDBusPendingCall>
22
23namespace KWin
24{
25
26KeyboardLayout::KeyboardLayout(Xkb *xkb, const KSharedConfigPtr &config)
27 : QObject()
28 , m_xkb(xkb)
29 , m_configGroup(config->group(QStringLiteral("Layout")))
30{
31}
32
34
35static QString translatedLayout(const QString &layout)
36{
37 return i18nd("xkeyboard-config", layout.toUtf8().constData());
38}
39
41{
42 QAction *switchKeyboardAction = new QAction(this);
43 switchKeyboardAction->setObjectName(QStringLiteral("Switch to Next Keyboard Layout"));
44 switchKeyboardAction->setProperty("componentName", QStringLiteral("KDE Keyboard Layout Switcher"));
45 switchKeyboardAction->setProperty("componentDisplayName", i18n("Keyboard Layout Switcher"));
46 const QKeySequence sequence = QKeySequence(Qt::META | Qt::ALT | Qt::Key_K);
47 KGlobalAccel::self()->setDefaultShortcut(switchKeyboardAction, QList<QKeySequence>({sequence}));
48 KGlobalAccel::self()->setShortcut(switchKeyboardAction, QList<QKeySequence>({sequence}));
49
50 connect(switchKeyboardAction, &QAction::triggered, this, &KeyboardLayout::switchToNextLayout);
51
52 QAction *switchLastUsedKeyboardAction = new QAction(this);
53 switchLastUsedKeyboardAction->setObjectName(QStringLiteral("Switch to Last-Used Keyboard Layout"));
54 switchLastUsedKeyboardAction->setProperty("componentName", QStringLiteral("KDE Keyboard Layout Switcher"));
55 switchLastUsedKeyboardAction->setProperty("componentDisplayName", i18n("Keyboard Layout Switcher"));
56 const QKeySequence sequenceLastUsed = QKeySequence(Qt::META | Qt::ALT | Qt::Key_L);
57 KGlobalAccel::self()->setDefaultShortcut(switchLastUsedKeyboardAction, QList<QKeySequence>({sequenceLastUsed}));
58 KGlobalAccel::self()->setShortcut(switchLastUsedKeyboardAction, QList<QKeySequence>({sequenceLastUsed}));
59
60 connect(switchLastUsedKeyboardAction, &QAction::triggered, this, &KeyboardLayout::switchToLastUsedLayout);
61
62 QDBusConnection::sessionBus().connect(QString(),
63 QStringLiteral("/Layouts"),
64 QStringLiteral("org.kde.keyboard"),
65 QStringLiteral("reloadConfig"),
66 this,
67 SLOT(reconfigure()));
68
69 reconfigure();
70}
71
72void KeyboardLayout::initDBusInterface()
73{
74 if (m_xkb->numberOfLayouts() <= 1) {
75 if (m_dbusInterface) {
76 m_dbusInterface->deleteLater();
77 m_dbusInterface = nullptr;
78 }
79 return;
80 }
81 if (m_dbusInterface) {
82 return;
83 }
84 m_dbusInterface = new KeyboardLayoutDBusInterface(m_xkb, m_configGroup, this);
85 connect(this, &KeyboardLayout::layoutChanged,
87 // TODO: the signal might be emitted even if the list didn't change
89}
90
92{
93 const quint32 previousLayout = m_xkb->currentLayout();
94 m_xkb->switchToNextLayout();
95 checkLayoutChange(previousLayout);
96}
97
99{
100 const quint32 previousLayout = m_xkb->currentLayout();
101 m_xkb->switchToPreviousLayout();
102 checkLayoutChange(previousLayout);
103}
104
105void KeyboardLayout::switchToLayout(xkb_layout_index_t index)
106{
107 const quint32 previousLayout = m_xkb->currentLayout();
108 m_xkb->switchToLayout(index);
109 checkLayoutChange(previousLayout);
110}
111
113{
114 const quint32 count = m_xkb->numberOfLayouts();
115 if (!m_lastUsedLayout.has_value() || *m_lastUsedLayout >= count) {
117 } else {
118 switchToLayout(*m_lastUsedLayout);
119 }
120}
121
122void KeyboardLayout::reconfigure()
123{
124 if (m_configGroup.isValid()) {
125 m_configGroup.config()->reparseConfiguration();
126 const QString policyKey = m_configGroup.readEntry("SwitchMode", QStringLiteral("Global"));
127 m_xkb->reconfigure();
128 if (!m_policy || m_policy->name() != policyKey) {
129 m_policy = KeyboardLayoutSwitching::Policy::create(m_xkb, this, m_configGroup, policyKey);
130 }
131 } else {
132 m_xkb->reconfigure();
133 }
134 resetLayout();
135}
136
138{
139 m_layout = m_xkb->currentLayout();
140 loadShortcuts();
141
142 initDBusInterface();
143 Q_EMIT layoutsReconfigured();
144}
145
146void KeyboardLayout::loadShortcuts()
147{
148 qDeleteAll(m_layoutShortcuts);
149 m_layoutShortcuts.clear();
150 const QString componentName = QStringLiteral("KDE Keyboard Layout Switcher");
151 const quint32 count = m_xkb->numberOfLayouts();
152 for (uint i = 0; i < count; ++i) {
153 // layout name is translated in the action name in keyboard kcm!
154 const QString action = QStringLiteral("Switch keyboard layout to %1").arg(translatedLayout(m_xkb->layoutName(i)));
155 const auto shortcuts = KGlobalAccel::self()->globalShortcut(componentName, action);
156 if (shortcuts.isEmpty()) {
157 continue;
158 }
159 QAction *a = new QAction(this);
160 a->setObjectName(action);
161 a->setProperty("componentName", componentName);
162 connect(a, &QAction::triggered, this,
163 std::bind(&KeyboardLayout::switchToLayout, this, i));
164 KGlobalAccel::self()->setShortcut(a, shortcuts, KGlobalAccel::Autoloading);
165 m_layoutShortcuts << a;
166 }
167}
168
169void KeyboardLayout::checkLayoutChange(uint previousLayout)
170{
171 // Get here on key event or DBus call.
172 // m_layout - layout saved last time OSD occurred
173 // previousLayout - actual layout just before potential layout change
174 // We need OSD if current layout deviates from any of these
175 const uint currentLayout = m_xkb->currentLayout();
176 if (m_layout != currentLayout || previousLayout != currentLayout) {
177 m_lastUsedLayout = std::optional<uint>{previousLayout};
178 m_layout = currentLayout;
179 notifyLayoutChange();
180 Q_EMIT layoutChanged(currentLayout);
181 }
182}
183
184void KeyboardLayout::notifyLayoutChange()
185{
186 // notify OSD service about the new layout
187 QDBusMessage msg = QDBusMessage::createMethodCall(
188 QStringLiteral("org.kde.plasmashell"),
189 QStringLiteral("/org/kde/osdService"),
190 QStringLiteral("org.kde.osdService"),
191 QStringLiteral("kbdLayoutChanged"));
192
193 msg << translatedLayout(m_xkb->layoutName());
194
195 QDBusConnection::sessionBus().asyncCall(msg);
196}
197
198static const QString s_keyboardService = QStringLiteral("org.kde.keyboard");
199static const QString s_keyboardObject = QStringLiteral("/Layouts");
200
202 : QObject(parent)
203 , m_xkb(xkb)
204 , m_configGroup(configGroup)
205 , m_keyboardLayout(parent)
206{
207 qRegisterMetaType<QList<LayoutNames>>("QList<LayoutNames>");
208 qDBusRegisterMetaType<LayoutNames>();
209 qDBusRegisterMetaType<QList<LayoutNames>>();
210
211 QDBusConnection::sessionBus().registerObject(s_keyboardObject, this, QDBusConnection::ExportAllSlots | QDBusConnection::ExportAllSignals);
212 QDBusConnection::sessionBus().registerService(s_keyboardService);
213}
214
216{
217 QDBusConnection::sessionBus().unregisterService(s_keyboardService);
218}
219
224
229
231{
232 const quint32 previousLayout = m_xkb->currentLayout();
233 if (!m_xkb->switchToLayout(index)) {
234 return false;
235 }
236 m_keyboardLayout->checkLayoutChange(previousLayout);
237 return true;
238}
239
241{
242 return m_xkb->currentLayout();
243}
244
245QList<KeyboardLayoutDBusInterface::LayoutNames> KeyboardLayoutDBusInterface::getLayoutsList() const
246{
247 // TODO: - should be handled by layout applet itself, it has nothing to do with KWin
248 const QStringList displayNames = m_configGroup.readEntry("DisplayNames", QStringList());
249
250 QList<LayoutNames> ret;
251 const int layoutsSize = m_xkb->numberOfLayouts();
252 const int displayNamesSize = displayNames.size();
253 for (int i = 0; i < layoutsSize; ++i) {
254 ret.append({m_xkb->layoutShortName(i), i < displayNamesSize ? displayNames.at(i) : QString(), translatedLayout(m_xkb->layoutName(i))});
255 }
256 return ret;
257}
258
259QDBusArgument &operator<<(QDBusArgument &argument, const KeyboardLayoutDBusInterface::LayoutNames &layoutNames)
260{
261 argument.beginStructure();
262 argument << layoutNames.shortName << layoutNames.displayName << layoutNames.longName;
263 argument.endStructure();
264 return argument;
265}
266
267const QDBusArgument &operator>>(const QDBusArgument &argument, KeyboardLayoutDBusInterface::LayoutNames &layoutNames)
268{
269 argument.beginStructure();
270 argument >> layoutNames.shortName >> layoutNames.displayName >> layoutNames.longName;
271 argument.endStructure();
272 return argument;
273}
274
275}
276
277#include "moc_keyboard_layout.cpp"
QList< LayoutNames > getLayoutsList() const
KeyboardLayoutDBusInterface(Xkb *xkb, const KConfigGroup &configGroup, KeyboardLayout *parent)
void layoutChanged(uint index)
void checkLayoutChange(uint previousLayout)
KeyboardLayout(Xkb *xkb, const KSharedConfigPtr &config)
~KeyboardLayout() override
static std::unique_ptr< Policy > create(Xkb *xkb, KeyboardLayout *layout, const KConfigGroup &config, const QString &policy)
void switchToNextLayout()
Definition xkb.cpp:610
QString layoutShortName(int index) const
Definition xkb.cpp:496
QString layoutName(xkb_layout_index_t index) const
Definition xkb.cpp:483
quint32 numberOfLayouts() const
Definition xkb.cpp:737
quint32 currentLayout() const
Definition xkb.h:85
void switchToPreviousLayout()
Definition xkb.cpp:620
bool switchToLayout(xkb_layout_index_t layout)
Definition xkb.cpp:629
void reconfigure()
Definition xkb.cpp:152
uint32_t xkb_layout_index_t
QDebug & operator<<(QDebug &s, const KWin::DrmConnector *obj)
const QDBusArgument & operator>>(const QDBusArgument &argument, KeyboardLayoutDBusInterface::LayoutNames &layoutNames)