File indexing completed on 2024-05-12 17:07:16
0001 /* 0002 SPDX-FileCopyrightText: 2010 Andriy Rysin <rysin@kde.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "layout_memory.h" 0008 #include "debug.h" 0009 0010 #include <KWindowSystem> 0011 #include <KX11Extras> 0012 0013 #include "xkb_helper.h" 0014 0015 LayoutMemory::LayoutMemory(const KeyboardConfig &keyboardConfig_) 0016 : prevLayoutList(X11Helper::getLayoutsList()) 0017 , keyboardConfig(keyboardConfig_) 0018 { 0019 registerListeners(); 0020 } 0021 0022 LayoutMemory::~LayoutMemory() 0023 { 0024 unregisterListeners(); 0025 } 0026 0027 void LayoutMemory::configChanged() 0028 { 0029 // this->layoutMap.clear(); // if needed this will be done on layoutMapChanged event 0030 unregisterListeners(); 0031 registerListeners(); 0032 } 0033 0034 void LayoutMemory::registerListeners() 0035 { 0036 if (keyboardConfig.switchingPolicy() == KeyboardConfig::SWITCH_POLICY_WINDOW || keyboardConfig.switchingPolicy() == KeyboardConfig::SWITCH_POLICY_APPLICATION) { 0037 connect(KX11Extras::self(), &KX11Extras::activeWindowChanged, this, &LayoutMemory::windowChanged); 0038 // connect(KWindowSystem::self(), SIGNAL(windowRemoved(WId)), this, SLOT(windowRemoved(WId))); 0039 } 0040 if (keyboardConfig.switchingPolicy() == KeyboardConfig::SWITCH_POLICY_DESKTOP) { 0041 connect(KX11Extras::self(), &KX11Extras::currentDesktopChanged, this, &LayoutMemory::desktopChanged); 0042 } 0043 } 0044 0045 void LayoutMemory::unregisterListeners() 0046 { 0047 disconnect(KX11Extras::self(), &KX11Extras::activeWindowChanged, this, &LayoutMemory::windowChanged); 0048 disconnect(KX11Extras::self(), &KX11Extras::currentDesktopChanged, this, &LayoutMemory::desktopChanged); 0049 // disconnect(KWindowSystem::self(), SIGNAL(windowRemoved(WId)), this, SLOT(windowRemoved(WId))); 0050 } 0051 0052 QString LayoutMemory::getCurrentMapKey() 0053 { 0054 switch (keyboardConfig.switchingPolicy()) { 0055 case KeyboardConfig::SWITCH_POLICY_WINDOW: { 0056 WId wid = KX11Extras::self()->activeWindow(); 0057 KWindowInfo winInfo(wid, NET::WMWindowType); 0058 NET::WindowType windowType = winInfo.windowType(NET::NormalMask | NET::DesktopMask | NET::DialogMask); 0059 qCDebug(KCM_KEYBOARD, ) << "window type" << windowType; 0060 0061 // we ignore desktop type so that our keyboard layout applet on desktop could change layout properly 0062 if (windowType == NET::Desktop) 0063 return previousLayoutMapKey; 0064 if (windowType != NET::Unknown && windowType != NET::Normal && windowType != NET::Dialog) 0065 return QString(); 0066 0067 return QString::number(wid); 0068 } 0069 case KeyboardConfig::SWITCH_POLICY_APPLICATION: { 0070 WId wid = KX11Extras::self()->activeWindow(); 0071 KWindowInfo winInfo(wid, NET::WMWindowType, NET::WM2WindowClass); 0072 NET::WindowType windowType = winInfo.windowType(NET::NormalMask | NET::DesktopMask | NET::DialogMask); 0073 qCDebug(KCM_KEYBOARD, ) << "window type" << windowType; 0074 0075 // we ignore desktop type so that our keyboard layout applet on desktop could change layout properly 0076 if (windowType == NET::Desktop) 0077 return previousLayoutMapKey; 0078 if (windowType != NET::Unknown && windowType != NET::Normal && windowType != NET::Dialog) 0079 return QString(); 0080 0081 // shall we use pid or window class ??? - class seems better (see e.g. https://bugs.kde.org/show_bug.cgi?id=245507) 0082 // for window class shall we use class.class or class.name? (seem class.class is a bit better - more app-oriented) 0083 qCDebug(KCM_KEYBOARD, ) << "New active window with class.class: " << winInfo.windowClassClass(); 0084 return QString(winInfo.windowClassClass()); 0085 // NETWinInfo winInfoForPid( QX11Info::display(), wid, QX11Info::appRootWindow(), NET::WMPid); 0086 // return QString::number(winInfoForPid.pid()); 0087 } 0088 case KeyboardConfig::SWITCH_POLICY_DESKTOP: 0089 return QString::number(KX11Extras::self()->currentDesktop()); 0090 default: 0091 return QString(); 0092 } 0093 } 0094 0095 static bool isExtraSubset(const QList<LayoutUnit> &allLayouts, const QList<LayoutUnit> &newList) 0096 { 0097 if (allLayouts.isEmpty() || newList.isEmpty() || allLayouts.first() != newList.first()) 0098 return false; 0099 for (const LayoutUnit &layoutUnit : newList) { 0100 if (!allLayouts.contains(layoutUnit)) 0101 return false; 0102 } 0103 return true; 0104 } 0105 0106 void LayoutMemory::layoutMapChanged() 0107 { 0108 QList<LayoutUnit> newLayoutList(X11Helper::getLayoutsList()); 0109 0110 if (prevLayoutList == newLayoutList) 0111 return; 0112 0113 qCDebug(KCM_KEYBOARD, ) << "Layout map change: " << LayoutSet::toString(prevLayoutList) << "-->" << LayoutSet::toString(newLayoutList); 0114 prevLayoutList = newLayoutList; 0115 0116 // TODO: need more thinking here on how to support external map resetting 0117 if (keyboardConfig.configureLayouts() && isExtraSubset(keyboardConfig.layouts, newLayoutList)) { 0118 qCDebug(KCM_KEYBOARD, ) << "Layout map change for extra layout"; 0119 layoutChanged(); // to remember new map for active "window" 0120 } else { 0121 if (newLayoutList != keyboardConfig.getDefaultLayouts()) { 0122 qCDebug(KCM_KEYBOARD, ) << "Layout map change from external source: clearing layout memory"; 0123 layoutMap.clear(); 0124 } 0125 } 0126 } 0127 0128 void LayoutMemory::layoutChanged() 0129 { 0130 QString layoutMapKey = getCurrentMapKey(); 0131 if (layoutMapKey.isEmpty()) 0132 return; 0133 0134 layoutMap[layoutMapKey] = X11Helper::getCurrentLayouts(); 0135 } 0136 0137 void LayoutMemory::setCurrentLayoutFromMap() 0138 { 0139 QString layoutMapKey = getCurrentMapKey(); 0140 if (layoutMapKey.isEmpty()) 0141 return; 0142 0143 if (!layoutMap.contains(layoutMapKey)) { 0144 // qCDebug(KCM_KEYBOARD, ) << "new key for layout map" << layoutMapKey; 0145 0146 if (!X11Helper::isDefaultLayout()) { 0147 // qCDebug(KCM_KEYBOARD, ) << "setting default layout for container key" << layoutMapKey; 0148 if (keyboardConfig.configureLayouts() && X11Helper::getLayoutsList() != keyboardConfig.getDefaultLayouts()) { 0149 XkbHelper::initializeKeyboardLayouts(keyboardConfig.getDefaultLayouts()); 0150 } 0151 X11Helper::setDefaultLayout(); 0152 } 0153 } else { 0154 LayoutSet layoutFromMap = layoutMap[layoutMapKey]; 0155 qCDebug(KCM_KEYBOARD, ) << "Setting layout map item" << layoutFromMap.currentLayout.toString() << "for container key" << layoutMapKey; 0156 0157 LayoutSet currentLayouts = X11Helper::getCurrentLayouts(); 0158 if (layoutFromMap.layouts != currentLayouts.layouts) { 0159 if (keyboardConfig.configureLayouts()) { 0160 XkbHelper::initializeKeyboardLayouts(layoutFromMap.layouts); 0161 } 0162 X11Helper::setLayout(layoutFromMap.currentLayout); 0163 } else if (layoutFromMap.currentLayout != currentLayouts.currentLayout) { 0164 X11Helper::setLayout(layoutFromMap.currentLayout); 0165 } 0166 } 0167 0168 previousLayoutMapKey = layoutMapKey; 0169 } 0170 0171 void LayoutMemory::windowChanged(WId /*wId*/) 0172 { 0173 setCurrentLayoutFromMap(); 0174 } 0175 0176 void LayoutMemory::desktopChanged(int /*desktop*/) 0177 { 0178 setCurrentLayoutFromMap(); 0179 }