Warning, file /plasma/kwin/src/keyboard_layout_switching.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2017 Martin Gräßlin <mgraesslin@kde.org> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 #include "keyboard_layout_switching.h" 0010 #include "deleted.h" 0011 #include "keyboard_layout.h" 0012 #include "virtualdesktops.h" 0013 #include "window.h" 0014 #include "workspace.h" 0015 #include "xkb.h" 0016 0017 namespace KWin 0018 { 0019 0020 namespace KeyboardLayoutSwitching 0021 { 0022 0023 Policy::Policy(Xkb *xkb, KeyboardLayout *layout, const KConfigGroup &config) 0024 : QObject(layout) 0025 , m_config(config) 0026 , m_xkb(xkb) 0027 , m_layout(layout) 0028 { 0029 connect(m_layout, &KeyboardLayout::layoutsReconfigured, this, &Policy::clearCache); 0030 connect(m_layout, &KeyboardLayout::layoutChanged, this, &Policy::layoutChanged); 0031 } 0032 0033 Policy::~Policy() = default; 0034 0035 void Policy::setLayout(uint index) 0036 { 0037 const uint previousLayout = m_xkb->currentLayout(); 0038 m_xkb->switchToLayout(index); 0039 const uint currentLayout = m_xkb->currentLayout(); 0040 if (previousLayout != currentLayout) { 0041 Q_EMIT m_layout->layoutChanged(currentLayout); 0042 } 0043 } 0044 0045 std::unique_ptr<Policy> Policy::create(Xkb *xkb, KeyboardLayout *layout, const KConfigGroup &config, const QString &policy) 0046 { 0047 if (policy.toLower() == QStringLiteral("desktop")) { 0048 return std::make_unique<VirtualDesktopPolicy>(xkb, layout, config); 0049 } 0050 if (policy.toLower() == QStringLiteral("window")) { 0051 return std::make_unique<WindowPolicy>(xkb, layout); 0052 } 0053 if (policy.toLower() == QStringLiteral("winclass")) { 0054 return std::make_unique<ApplicationPolicy>(xkb, layout, config); 0055 } 0056 return std::make_unique<GlobalPolicy>(xkb, layout, config); 0057 } 0058 0059 const char Policy::defaultLayoutEntryKeyPrefix[] = "LayoutDefault"; 0060 const QString Policy::defaultLayoutEntryKey() const 0061 { 0062 return QLatin1String(defaultLayoutEntryKeyPrefix) % name() % QLatin1Char('_'); 0063 } 0064 0065 void Policy::clearLayouts() 0066 { 0067 const QStringList layoutEntryList = m_config.keyList().filter(defaultLayoutEntryKeyPrefix); 0068 for (const auto &layoutEntry : layoutEntryList) { 0069 m_config.deleteEntry(layoutEntry); 0070 } 0071 } 0072 0073 const QString GlobalPolicy::defaultLayoutEntryKey() const 0074 { 0075 return QLatin1String(defaultLayoutEntryKeyPrefix) % name(); 0076 } 0077 0078 GlobalPolicy::GlobalPolicy(Xkb *xkb, KeyboardLayout *_layout, const KConfigGroup &config) 0079 : Policy(xkb, _layout, config) 0080 { 0081 connect(workspace()->sessionManager(), &SessionManager::prepareSessionSaveRequested, this, [this, xkb](const QString &name) { 0082 clearLayouts(); 0083 if (const uint layout = xkb->currentLayout()) { 0084 m_config.writeEntry(defaultLayoutEntryKey(), layout); 0085 } 0086 }); 0087 0088 connect(workspace()->sessionManager(), &SessionManager::loadSessionRequested, this, [this, xkb](const QString &name) { 0089 if (xkb->numberOfLayouts() > 1) { 0090 setLayout(m_config.readEntry(defaultLayoutEntryKey(), 0)); 0091 } 0092 }); 0093 } 0094 0095 GlobalPolicy::~GlobalPolicy() = default; 0096 0097 VirtualDesktopPolicy::VirtualDesktopPolicy(Xkb *xkb, KeyboardLayout *layout, const KConfigGroup &config) 0098 : Policy(xkb, layout, config) 0099 { 0100 connect(VirtualDesktopManager::self(), &VirtualDesktopManager::currentChanged, 0101 this, &VirtualDesktopPolicy::desktopChanged); 0102 0103 connect(workspace()->sessionManager(), &SessionManager::prepareSessionSaveRequested, this, [this](const QString &name) { 0104 clearLayouts(); 0105 0106 for (auto i = m_layouts.constBegin(); i != m_layouts.constEnd(); ++i) { 0107 if (const uint layout = *i) { 0108 m_config.writeEntry(defaultLayoutEntryKey() % QString::number(i.key()->x11DesktopNumber()), layout); 0109 } 0110 } 0111 }); 0112 0113 connect(workspace()->sessionManager(), &SessionManager::loadSessionRequested, this, [this, xkb](const QString &name) { 0114 if (xkb->numberOfLayouts() > 1) { 0115 const auto &desktops = VirtualDesktopManager::self()->desktops(); 0116 for (KWin::VirtualDesktop *const desktop : desktops) { 0117 const uint layout = m_config.readEntry(defaultLayoutEntryKey() % QString::number(desktop->x11DesktopNumber()), 0u); 0118 if (layout) { 0119 m_layouts.insert(desktop, layout); 0120 connect(desktop, &VirtualDesktop::aboutToBeDestroyed, this, [this, desktop]() { 0121 m_layouts.remove(desktop); 0122 }); 0123 } 0124 } 0125 desktopChanged(); 0126 } 0127 }); 0128 } 0129 0130 VirtualDesktopPolicy::~VirtualDesktopPolicy() = default; 0131 0132 void VirtualDesktopPolicy::clearCache() 0133 { 0134 m_layouts.clear(); 0135 } 0136 0137 namespace 0138 { 0139 template<typename T, typename U> 0140 quint32 getLayout(const T &layouts, const U &reference) 0141 { 0142 auto it = layouts.constFind(reference); 0143 if (it == layouts.constEnd()) { 0144 return 0; 0145 } else { 0146 return it.value(); 0147 } 0148 } 0149 } 0150 0151 void VirtualDesktopPolicy::desktopChanged() 0152 { 0153 auto d = VirtualDesktopManager::self()->currentDesktop(); 0154 if (!d) { 0155 return; 0156 } 0157 setLayout(getLayout(m_layouts, d)); 0158 } 0159 0160 void VirtualDesktopPolicy::layoutChanged(uint index) 0161 { 0162 auto d = VirtualDesktopManager::self()->currentDesktop(); 0163 if (!d) { 0164 return; 0165 } 0166 auto it = m_layouts.find(d); 0167 if (it == m_layouts.end()) { 0168 m_layouts.insert(d, index); 0169 connect(d, &VirtualDesktop::aboutToBeDestroyed, this, [this, d]() { 0170 m_layouts.remove(d); 0171 }); 0172 } else { 0173 if (it.value() == index) { 0174 return; 0175 } 0176 it.value() = index; 0177 } 0178 } 0179 0180 WindowPolicy::WindowPolicy(KWin::Xkb *xkb, KWin::KeyboardLayout *layout) 0181 : Policy(xkb, layout) 0182 { 0183 connect(workspace(), &Workspace::windowActivated, this, [this](Window *window) { 0184 if (!window) { 0185 return; 0186 } 0187 // ignore some special types 0188 if (window->isDesktop() || window->isDock()) { 0189 return; 0190 } 0191 setLayout(getLayout(m_layouts, window)); 0192 }); 0193 } 0194 0195 WindowPolicy::~WindowPolicy() 0196 { 0197 } 0198 0199 void WindowPolicy::clearCache() 0200 { 0201 m_layouts.clear(); 0202 } 0203 0204 void WindowPolicy::layoutChanged(uint index) 0205 { 0206 auto window = workspace()->activeWindow(); 0207 if (!window) { 0208 return; 0209 } 0210 // ignore some special types 0211 if (window->isDesktop() || window->isDock()) { 0212 return; 0213 } 0214 0215 auto it = m_layouts.find(window); 0216 if (it == m_layouts.end()) { 0217 m_layouts.insert(window, index); 0218 connect(window, &Window::windowClosed, this, [this, window]() { 0219 m_layouts.remove(window); 0220 }); 0221 } else { 0222 if (it.value() == index) { 0223 return; 0224 } 0225 it.value() = index; 0226 } 0227 } 0228 0229 ApplicationPolicy::ApplicationPolicy(KWin::Xkb *xkb, KWin::KeyboardLayout *layout, const KConfigGroup &config) 0230 : Policy(xkb, layout, config) 0231 { 0232 connect(workspace(), &Workspace::windowActivated, this, &ApplicationPolicy::windowActivated); 0233 0234 connect(workspace()->sessionManager(), &SessionManager::prepareSessionSaveRequested, this, [this](const QString &name) { 0235 clearLayouts(); 0236 0237 for (auto i = m_layouts.constBegin(); i != m_layouts.constEnd(); ++i) { 0238 if (const uint layout = *i) { 0239 const QString desktopFileName = i.key()->desktopFileName(); 0240 if (!desktopFileName.isEmpty()) { 0241 m_config.writeEntry(defaultLayoutEntryKey() % desktopFileName, layout); 0242 } 0243 } 0244 } 0245 }); 0246 0247 connect(workspace()->sessionManager(), &SessionManager::loadSessionRequested, this, [this, xkb](const QString &name) { 0248 if (xkb->numberOfLayouts() > 1) { 0249 const QString keyPrefix = defaultLayoutEntryKey(); 0250 const QStringList keyList = m_config.keyList().filter(keyPrefix); 0251 for (const QString &key : keyList) { 0252 m_layoutsRestored.insert( 0253 QStringView(key).mid(keyPrefix.size()).toLatin1(), 0254 m_config.readEntry(key, 0)); 0255 } 0256 } 0257 m_layoutsRestored.squeeze(); 0258 }); 0259 } 0260 0261 ApplicationPolicy::~ApplicationPolicy() 0262 { 0263 } 0264 0265 void ApplicationPolicy::windowActivated(Window *window) 0266 { 0267 if (!window) { 0268 return; 0269 } 0270 // ignore some special types 0271 if (window->isDesktop() || window->isDock()) { 0272 return; 0273 } 0274 auto it = m_layouts.constFind(window); 0275 if (it != m_layouts.constEnd()) { 0276 setLayout(it.value()); 0277 return; 0278 }; 0279 for (it = m_layouts.constBegin(); it != m_layouts.constEnd(); it++) { 0280 if (Window::belongToSameApplication(window, it.key())) { 0281 const uint layout = it.value(); 0282 setLayout(layout); 0283 layoutChanged(layout); 0284 return; 0285 } 0286 } 0287 setLayout(m_layoutsRestored.take(window->desktopFileName())); 0288 if (const uint index = m_xkb->currentLayout()) { 0289 layoutChanged(index); 0290 } 0291 } 0292 0293 void ApplicationPolicy::clearCache() 0294 { 0295 m_layouts.clear(); 0296 } 0297 0298 void ApplicationPolicy::layoutChanged(uint index) 0299 { 0300 auto window = workspace()->activeWindow(); 0301 if (!window) { 0302 return; 0303 } 0304 // ignore some special types 0305 if (window->isDesktop() || window->isDock()) { 0306 return; 0307 } 0308 0309 auto it = m_layouts.find(window); 0310 if (it == m_layouts.end()) { 0311 m_layouts.insert(window, index); 0312 connect(window, &Window::windowClosed, this, [this, window]() { 0313 m_layouts.remove(window); 0314 }); 0315 } else { 0316 if (it.value() == index) { 0317 return; 0318 } 0319 it.value() = index; 0320 } 0321 // update all layouts for the application 0322 for (it = m_layouts.begin(); it != m_layouts.end(); it++) { 0323 if (Window::belongToSameApplication(it.key(), window)) { 0324 it.value() = index; 0325 } 0326 } 0327 } 0328 0329 } 0330 }