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