KWin
Loading...
Searching...
No Matches
keyboard_layout_switching.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: 2017 Martin Gräßlin <mgraesslin@kde.org>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
10#include "keyboard_layout.h"
11#include "virtualdesktops.h"
12#include "window.h"
13#include "workspace.h"
14#include "xkb.h"
15
16namespace KWin
17{
18
19namespace KeyboardLayoutSwitching
20{
21
22Policy::Policy(Xkb *xkb, KeyboardLayout *layout, const KConfigGroup &config)
23 : QObject(layout)
24 , m_config(config)
25 , m_xkb(xkb)
26 , m_layout(layout)
27{
29 connect(m_layout, &KeyboardLayout::layoutChanged, this, &Policy::layoutChanged);
30}
31
32Policy::~Policy() = default;
33
34void Policy::setLayout(uint index)
35{
36 const uint previousLayout = m_xkb->currentLayout();
37 m_xkb->switchToLayout(index);
38 const uint currentLayout = m_xkb->currentLayout();
39 if (previousLayout != currentLayout) {
40 Q_EMIT m_layout->layoutChanged(currentLayout);
41 }
42}
43
44std::unique_ptr<Policy> Policy::create(Xkb *xkb, KeyboardLayout *layout, const KConfigGroup &config, const QString &policy)
45{
46 if (policy.toLower() == QStringLiteral("desktop")) {
47 return std::make_unique<VirtualDesktopPolicy>(xkb, layout, config);
48 }
49 if (policy.toLower() == QStringLiteral("window")) {
50 return std::make_unique<WindowPolicy>(xkb, layout);
51 }
52 if (policy.toLower() == QStringLiteral("winclass")) {
53 return std::make_unique<ApplicationPolicy>(xkb, layout, config);
54 }
55 return std::make_unique<GlobalPolicy>(xkb, layout, config);
56}
57
58const char Policy::defaultLayoutEntryKeyPrefix[] = "LayoutDefault";
59const QString Policy::defaultLayoutEntryKey() const
60{
61 return QLatin1String(defaultLayoutEntryKeyPrefix) % name() % QLatin1Char('_');
62}
63
65{
66 const QStringList layoutEntryList = m_config.keyList().filter(defaultLayoutEntryKeyPrefix);
67 for (const auto &layoutEntry : layoutEntryList) {
68 m_config.deleteEntry(layoutEntry);
69 }
70}
71
72const QString GlobalPolicy::defaultLayoutEntryKey() const
73{
74 return QLatin1String(defaultLayoutEntryKeyPrefix) % name();
75}
76
77GlobalPolicy::GlobalPolicy(Xkb *xkb, KeyboardLayout *_layout, const KConfigGroup &config)
78 : Policy(xkb, _layout, config)
79{
80 connect(workspace()->sessionManager(), &SessionManager::prepareSessionSaveRequested, this, [this, xkb](const QString &name) {
82 if (const uint layout = xkb->currentLayout()) {
83 m_config.writeEntry(defaultLayoutEntryKey(), layout);
84 }
85 });
86
87 connect(workspace()->sessionManager(), &SessionManager::loadSessionRequested, this, [this, xkb](const QString &name) {
88 if (xkb->numberOfLayouts() > 1) {
89 setLayout(m_config.readEntry(defaultLayoutEntryKey(), 0));
90 }
91 });
92}
93
95
96VirtualDesktopPolicy::VirtualDesktopPolicy(Xkb *xkb, KeyboardLayout *layout, const KConfigGroup &config)
97 : Policy(xkb, layout, config)
98{
99 connect(VirtualDesktopManager::self(), &VirtualDesktopManager::currentChanged,
100 this, &VirtualDesktopPolicy::desktopChanged);
101
102 connect(workspace()->sessionManager(), &SessionManager::prepareSessionSaveRequested, this, [this](const QString &name) {
103 clearLayouts();
104
105 for (auto i = m_layouts.constBegin(); i != m_layouts.constEnd(); ++i) {
106 if (const uint layout = *i) {
107 m_config.writeEntry(defaultLayoutEntryKey() % QString::number(i.key()->x11DesktopNumber()), layout);
108 }
109 }
110 });
111
112 connect(workspace()->sessionManager(), &SessionManager::loadSessionRequested, this, [this, xkb](const QString &name) {
113 if (xkb->numberOfLayouts() > 1) {
114 const auto &desktops = VirtualDesktopManager::self()->desktops();
115 for (KWin::VirtualDesktop *const desktop : desktops) {
116 const uint layout = m_config.readEntry(defaultLayoutEntryKey() % QString::number(desktop->x11DesktopNumber()), 0u);
117 if (layout) {
118 m_layouts.insert(desktop, layout);
119 connect(desktop, &VirtualDesktop::aboutToBeDestroyed, this, [this, desktop]() {
120 m_layouts.remove(desktop);
121 });
122 }
123 }
124 desktopChanged();
125 }
126 });
127}
128
129VirtualDesktopPolicy::~VirtualDesktopPolicy() = default;
130
131void VirtualDesktopPolicy::clearCache()
132{
133 m_layouts.clear();
134}
135
136namespace
137{
138template<typename T, typename U>
139quint32 getLayout(const T &layouts, const U &reference)
140{
141 auto it = layouts.constFind(reference);
142 if (it == layouts.constEnd()) {
143 return 0;
144 } else {
145 return it.value();
146 }
147}
148}
149
150void VirtualDesktopPolicy::desktopChanged()
151{
152 auto d = VirtualDesktopManager::self()->currentDesktop();
153 if (!d) {
154 return;
155 }
156 setLayout(getLayout(m_layouts, d));
157}
158
159void VirtualDesktopPolicy::layoutChanged(uint index)
160{
161 auto d = VirtualDesktopManager::self()->currentDesktop();
162 if (!d) {
163 return;
164 }
165 auto it = m_layouts.find(d);
166 if (it == m_layouts.end()) {
167 m_layouts.insert(d, index);
168 connect(d, &VirtualDesktop::aboutToBeDestroyed, this, [this, d]() {
169 m_layouts.remove(d);
170 });
171 } else {
172 if (it.value() == index) {
173 return;
174 }
175 it.value() = index;
176 }
177}
178
179WindowPolicy::WindowPolicy(KWin::Xkb *xkb, KWin::KeyboardLayout *layout)
180 : Policy(xkb, layout)
181{
182 connect(workspace(), &Workspace::windowActivated, this, [this](Window *window) {
183 if (!window) {
184 return;
185 }
186 // ignore some special types
187 if (window->isDesktop() || window->isDock()) {
188 return;
189 }
190 setLayout(getLayout(m_layouts, window));
191 });
192}
193
197
199{
200 m_layouts.clear();
201}
202
204{
205 auto window = workspace()->activeWindow();
206 if (!window) {
207 return;
208 }
209 // ignore some special types
210 if (window->isDesktop() || window->isDock()) {
211 return;
212 }
213
214 auto it = m_layouts.find(window);
215 if (it == m_layouts.end()) {
216 m_layouts.insert(window, index);
217 connect(window, &Window::closed, this, [this, window]() {
218 m_layouts.remove(window);
219 });
220 } else {
221 if (it.value() == index) {
222 return;
223 }
224 it.value() = index;
225 }
226}
227
229 : Policy(xkb, layout, config)
230{
231 connect(workspace(), &Workspace::windowActivated, this, &ApplicationPolicy::windowActivated);
232
233 connect(workspace()->sessionManager(), &SessionManager::prepareSessionSaveRequested, this, [this](const QString &name) {
234 clearLayouts();
235
236 for (auto i = m_layouts.constBegin(); i != m_layouts.constEnd(); ++i) {
237 if (const uint layout = *i) {
238 const QString desktopFileName = i.key()->desktopFileName();
239 if (!desktopFileName.isEmpty()) {
240 m_config.writeEntry(defaultLayoutEntryKey() % desktopFileName, layout);
241 }
242 }
243 }
244 });
245
246 connect(workspace()->sessionManager(), &SessionManager::loadSessionRequested, this, [this, xkb](const QString &name) {
247 if (xkb->numberOfLayouts() > 1) {
248 const QString keyPrefix = defaultLayoutEntryKey();
249 const QStringList keyList = m_config.keyList().filter(keyPrefix);
250 for (const QString &key : keyList) {
251 m_layoutsRestored.insert(
252 QStringView(key).mid(keyPrefix.size()).toLatin1(),
253 m_config.readEntry(key, 0));
254 }
255 }
256 m_layoutsRestored.squeeze();
257 });
258}
259
260ApplicationPolicy::~ApplicationPolicy()
261{
262}
263
264void ApplicationPolicy::windowActivated(Window *window)
265{
266 if (!window) {
267 return;
268 }
269 // ignore some special types
270 if (window->isDesktop() || window->isDock()) {
271 return;
272 }
273 auto it = m_layouts.constFind(window);
274 if (it != m_layouts.constEnd()) {
275 setLayout(it.value());
276 return;
277 };
278 for (it = m_layouts.constBegin(); it != m_layouts.constEnd(); it++) {
279 if (Window::belongToSameApplication(window, it.key())) {
280 const uint layout = it.value();
281 setLayout(layout);
282 layoutChanged(layout);
283 return;
284 }
285 }
286 setLayout(m_layoutsRestored.take(window->desktopFileName()));
287 if (const uint index = m_xkb->currentLayout()) {
288 layoutChanged(index);
289 }
290}
291
292void ApplicationPolicy::clearCache()
293{
294 m_layouts.clear();
295}
296
297void ApplicationPolicy::layoutChanged(uint index)
298{
299 auto window = workspace()->activeWindow();
300 if (!window) {
301 return;
302 }
303 // ignore some special types
304 if (window->isDesktop() || window->isDock()) {
305 return;
306 }
307
308 auto it = m_layouts.find(window);
309 if (it == m_layouts.end()) {
310 m_layouts.insert(window, index);
311 connect(window, &Window::closed, this, [this, window]() {
312 m_layouts.remove(window);
313 });
314 } else {
315 if (it.value() == index) {
316 return;
317 }
318 it.value() = index;
319 }
320 // update all layouts for the application
321 for (it = m_layouts.begin(); it != m_layouts.end(); it++) {
322 if (Window::belongToSameApplication(it.key(), window)) {
323 it.value() = index;
324 }
325 }
326}
327
328}
329}
330
331#include "moc_keyboard_layout_switching.cpp"
void layoutChanged(uint index)
ApplicationPolicy(Xkb *xkb, KeyboardLayout *layout, const KConfigGroup &config)
GlobalPolicy(Xkb *xkb, KeyboardLayout *layout, const KConfigGroup &config)
virtual const QString defaultLayoutEntryKey() const
static std::unique_ptr< Policy > create(Xkb *xkb, KeyboardLayout *layout, const KConfigGroup &config, const QString &policy)
Policy(Xkb *xkb, KeyboardLayout *layout, const KConfigGroup &config=KConfigGroup())
virtual void layoutChanged(uint index)=0
virtual QString name() const =0
VirtualDesktopPolicy(Xkb *xkb, KeyboardLayout *layout, const KConfigGroup &config)
void loadSessionRequested(const QString &name)
void prepareSessionSaveRequested(const QString &name)
void currentChanged(KWin::VirtualDesktop *previousDesktop, KWin::VirtualDesktop *newDesktop)
QString desktopFileName
Definition window.h:501
static bool belongToSameApplication(const Window *c1, const Window *c2, SameApplicationChecks checks=SameApplicationChecks())
Definition window.cpp:426
bool isDesktop() const
Definition window.h:1922
bool isDock() const
Definition window.h:1927
Window * activeWindow() const
Definition workspace.h:767
void windowActivated(KWin::Window *)
quint32 numberOfLayouts() const
Definition xkb.cpp:737
quint32 currentLayout() const
Definition xkb.h:85
bool switchToLayout(xkb_layout_index_t layout)
Definition xkb.cpp:629
Workspace * workspace()
Definition workspace.h:830