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