File indexing completed on 2024-05-19 16:34:55
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 1999, 2000 Matthias Ettrich <ettrich@kde.org> 0006 SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org> 0007 SPDX-FileCopyrightText: 2009 Martin Gräßlin <mgraesslin@kde.org> 0008 0009 SPDX-License-Identifier: GPL-2.0-or-later 0010 */ 0011 0012 //#define QT_CLEAN_NAMESPACE 0013 // own 0014 #include "tabbox.h" 0015 // tabbox 0016 #include "tabbox/clientmodel.h" 0017 #include "tabbox/desktopchain.h" 0018 #include "tabbox/desktopmodel.h" 0019 #include "tabbox/tabbox_logging.h" 0020 #include "tabbox/tabboxconfig.h" 0021 #include "tabbox/x11_filter.h" 0022 // kwin 0023 #if KWIN_BUILD_ACTIVITIES 0024 #include "activities.h" 0025 #endif 0026 #include "composite.h" 0027 #include "effects.h" 0028 #include "focuschain.h" 0029 #include "input.h" 0030 #include "keyboard_input.h" 0031 #include "pointer_input.h" 0032 #include "screenedge.h" 0033 #include "unmanaged.h" 0034 #include "utils/xcbutils.h" 0035 #include "virtualdesktops.h" 0036 #include "workspace.h" 0037 #include "x11window.h" 0038 // Qt 0039 #include <QAction> 0040 #include <QKeyEvent> 0041 // KDE 0042 #include <KConfig> 0043 #include <KConfigGroup> 0044 #include <KGlobalAccel> 0045 #include <KLazyLocalizedString> 0046 #include <KLocalizedString> 0047 #include <kkeyserver.h> 0048 // X11 0049 #include <X11/keysym.h> 0050 #include <X11/keysymdef.h> 0051 // xcb 0052 #include <xcb/xcb_keysyms.h> 0053 0054 // specify externals before namespace 0055 0056 namespace KWin 0057 { 0058 0059 namespace TabBox 0060 { 0061 0062 TabBoxHandlerImpl::TabBoxHandlerImpl(TabBox *tabBox) 0063 : TabBoxHandler(tabBox) 0064 , m_tabBox(tabBox) 0065 , m_desktopFocusChain(new DesktopChainManager(this)) 0066 { 0067 // connects for DesktopFocusChainManager 0068 VirtualDesktopManager *vds = VirtualDesktopManager::self(); 0069 connect(vds, &VirtualDesktopManager::countChanged, m_desktopFocusChain, &DesktopChainManager::resize); 0070 connect(vds, &VirtualDesktopManager::currentChanged, m_desktopFocusChain, &DesktopChainManager::addDesktop); 0071 #if KWIN_BUILD_ACTIVITIES 0072 if (Workspace::self()->activities()) { 0073 connect(Workspace::self()->activities(), &Activities::currentChanged, m_desktopFocusChain, &DesktopChainManager::useChain); 0074 } 0075 #endif 0076 } 0077 0078 TabBoxHandlerImpl::~TabBoxHandlerImpl() 0079 { 0080 } 0081 0082 int TabBoxHandlerImpl::activeScreen() const 0083 { 0084 return workspace()->outputs().indexOf(workspace()->activeOutput()); 0085 } 0086 0087 int TabBoxHandlerImpl::currentDesktop() const 0088 { 0089 return VirtualDesktopManager::self()->current(); 0090 } 0091 0092 QString TabBoxHandlerImpl::desktopName(TabBoxClient *client) const 0093 { 0094 if (TabBoxClientImpl *c = static_cast<TabBoxClientImpl *>(client)) { 0095 if (!c->client()->isOnAllDesktops()) { 0096 return desktopName(c->client()->desktop()); 0097 } 0098 } 0099 return desktopName(VirtualDesktopManager::self()->current()); 0100 } 0101 0102 QString TabBoxHandlerImpl::desktopName(int desktop) const 0103 { 0104 const VirtualDesktop *vd = VirtualDesktopManager::self()->desktopForX11Id(desktop); 0105 return vd ? vd->name() : QString(); 0106 } 0107 0108 QWeakPointer<TabBoxClient> TabBoxHandlerImpl::nextClientFocusChain(TabBoxClient *client) const 0109 { 0110 if (TabBoxClientImpl *c = static_cast<TabBoxClientImpl *>(client)) { 0111 auto next = Workspace::self()->focusChain()->nextMostRecentlyUsed(c->client()); 0112 if (next) { 0113 return qWeakPointerCast<TabBoxClient, TabBoxClientImpl>(next->tabBoxClient()); 0114 } 0115 } 0116 return QWeakPointer<TabBoxClient>(); 0117 } 0118 0119 QWeakPointer<TabBoxClient> TabBoxHandlerImpl::firstClientFocusChain() const 0120 { 0121 if (auto c = Workspace::self()->focusChain()->firstMostRecentlyUsed()) { 0122 return qWeakPointerCast<TabBoxClient, TabBoxClientImpl>(c->tabBoxClient()); 0123 } else { 0124 return QWeakPointer<TabBoxClient>(); 0125 } 0126 } 0127 0128 bool TabBoxHandlerImpl::isInFocusChain(TabBoxClient *client) const 0129 { 0130 if (TabBoxClientImpl *c = static_cast<TabBoxClientImpl *>(client)) { 0131 return Workspace::self()->focusChain()->contains(c->client()); 0132 } 0133 return false; 0134 } 0135 0136 int TabBoxHandlerImpl::nextDesktopFocusChain(int desktop) const 0137 { 0138 return m_desktopFocusChain->next(desktop); 0139 } 0140 0141 int TabBoxHandlerImpl::numberOfDesktops() const 0142 { 0143 return VirtualDesktopManager::self()->count(); 0144 } 0145 0146 QWeakPointer<TabBoxClient> TabBoxHandlerImpl::activeClient() const 0147 { 0148 if (Workspace::self()->activeWindow()) { 0149 return qWeakPointerCast<TabBoxClient, TabBoxClientImpl>(Workspace::self()->activeWindow()->tabBoxClient()); 0150 } else { 0151 return QWeakPointer<TabBoxClient>(); 0152 } 0153 } 0154 0155 bool TabBoxHandlerImpl::checkDesktop(TabBoxClient *client, int desktop) const 0156 { 0157 auto current = (static_cast<TabBoxClientImpl *>(client))->client(); 0158 0159 switch (config().clientDesktopMode()) { 0160 case TabBoxConfig::AllDesktopsClients: 0161 return true; 0162 case TabBoxConfig::ExcludeCurrentDesktopClients: 0163 return !current->isOnDesktop(desktop); 0164 default: // TabBoxConfig::OnlyCurrentDesktopClients 0165 return current->isOnDesktop(desktop); 0166 } 0167 } 0168 0169 bool TabBoxHandlerImpl::checkActivity(TabBoxClient *client) const 0170 { 0171 auto current = (static_cast<TabBoxClientImpl *>(client))->client(); 0172 0173 switch (config().clientActivitiesMode()) { 0174 case TabBoxConfig::AllActivitiesClients: 0175 return true; 0176 case TabBoxConfig::ExcludeCurrentActivityClients: 0177 return !current->isOnCurrentActivity(); 0178 default: // TabBoxConfig::OnlyCurrentActivityClients 0179 return current->isOnCurrentActivity(); 0180 } 0181 } 0182 0183 bool TabBoxHandlerImpl::checkApplications(TabBoxClient *client) const 0184 { 0185 auto current = (static_cast<TabBoxClientImpl *>(client))->client(); 0186 TabBoxClientImpl *c; 0187 QListIterator<QWeakPointer<TabBoxClient>> i(clientList()); 0188 0189 switch (config().clientApplicationsMode()) { 0190 case TabBoxConfig::OneWindowPerApplication: 0191 // check if the list already contains an entry of this application 0192 while (i.hasNext()) { 0193 QSharedPointer<TabBoxClient> client = i.next().toStrongRef(); 0194 if (!client) { 0195 continue; 0196 } 0197 if ((c = dynamic_cast<TabBoxClientImpl *>(client.data()))) { 0198 if (Window::belongToSameApplication(c->client(), current, Window::SameApplicationCheck::AllowCrossProcesses)) { 0199 return false; 0200 } 0201 } 0202 } 0203 return true; 0204 case TabBoxConfig::AllWindowsCurrentApplication: { 0205 QSharedPointer<TabBoxClient> pointer = tabBox->activeClient().toStrongRef(); 0206 if (!pointer) { 0207 return false; 0208 } 0209 if ((c = dynamic_cast<TabBoxClientImpl *>(pointer.data()))) { 0210 if (Window::belongToSameApplication(c->client(), current, Window::SameApplicationCheck::AllowCrossProcesses)) { 0211 return true; 0212 } 0213 } 0214 return false; 0215 } 0216 default: // TabBoxConfig::AllWindowsAllApplications 0217 return true; 0218 } 0219 } 0220 0221 bool TabBoxHandlerImpl::checkMinimized(TabBoxClient *client) const 0222 { 0223 switch (config().clientMinimizedMode()) { 0224 case TabBoxConfig::ExcludeMinimizedClients: 0225 return !client->isMinimized(); 0226 case TabBoxConfig::OnlyMinimizedClients: 0227 return client->isMinimized(); 0228 default: // TabBoxConfig::IgnoreMinimizedStatus 0229 return true; 0230 } 0231 } 0232 0233 bool TabBoxHandlerImpl::checkMultiScreen(TabBoxClient *client) const 0234 { 0235 auto current = (static_cast<TabBoxClientImpl *>(client))->client(); 0236 0237 switch (config().clientMultiScreenMode()) { 0238 case TabBoxConfig::IgnoreMultiScreen: 0239 return true; 0240 case TabBoxConfig::ExcludeCurrentScreenClients: 0241 return current->output() != workspace()->activeOutput(); 0242 default: // TabBoxConfig::OnlyCurrentScreenClients 0243 return current->output() == workspace()->activeOutput(); 0244 } 0245 } 0246 0247 QWeakPointer<TabBoxClient> TabBoxHandlerImpl::clientToAddToList(TabBoxClient *client, int desktop) const 0248 { 0249 if (!client) { 0250 return QWeakPointer<TabBoxClient>(); 0251 } 0252 Window *ret = nullptr; 0253 Window *current = (static_cast<TabBoxClientImpl *>(client))->client(); 0254 0255 bool addClient = checkDesktop(client, desktop) 0256 && checkActivity(client) 0257 && checkApplications(client) 0258 && checkMinimized(client) 0259 && checkMultiScreen(client); 0260 addClient = addClient && current->wantsTabFocus() && !current->skipSwitcher(); 0261 if (addClient) { 0262 // don't add windows that have modal dialogs 0263 Window *modal = current->findModal(); 0264 if (modal == nullptr || modal == current) { 0265 ret = current; 0266 } else if (!clientList().contains(qWeakPointerCast<TabBoxClient, TabBoxClientImpl>(modal->tabBoxClient()))) { 0267 ret = modal; 0268 } else { 0269 // nothing 0270 } 0271 } 0272 if (ret) { 0273 return qWeakPointerCast<TabBoxClient, TabBoxClientImpl>(ret->tabBoxClient()); 0274 } else { 0275 return QWeakPointer<TabBoxClient>(); 0276 } 0277 } 0278 0279 TabBoxClientList TabBoxHandlerImpl::stackingOrder() const 0280 { 0281 const QList<Window *> stacking = Workspace::self()->stackingOrder(); 0282 TabBoxClientList ret; 0283 for (Window *window : stacking) { 0284 if (window->isClient()) { 0285 ret.append(qWeakPointerCast<TabBoxClient, TabBoxClientImpl>(window->tabBoxClient())); 0286 } 0287 } 0288 return ret; 0289 } 0290 0291 bool TabBoxHandlerImpl::isKWinCompositing() const 0292 { 0293 return Compositor::compositing(); 0294 } 0295 0296 void TabBoxHandlerImpl::raiseClient(TabBoxClient *c) const 0297 { 0298 Workspace::self()->raiseWindow(static_cast<TabBoxClientImpl *>(c)->client()); 0299 } 0300 0301 void TabBoxHandlerImpl::restack(TabBoxClient *c, TabBoxClient *under) 0302 { 0303 Workspace::self()->restack(static_cast<TabBoxClientImpl *>(c)->client(), 0304 static_cast<TabBoxClientImpl *>(under)->client(), true); 0305 } 0306 0307 void TabBoxHandlerImpl::elevateClient(TabBoxClient *c, QWindow *tabbox, bool b) const 0308 { 0309 auto cl = static_cast<TabBoxClientImpl *>(c)->client(); 0310 cl->elevate(b); 0311 if (Window *w = Workspace::self()->findInternal(tabbox)) { 0312 w->elevate(b); 0313 } 0314 } 0315 0316 void TabBoxHandlerImpl::shadeClient(TabBoxClient *c, bool b) const 0317 { 0318 Window *client = static_cast<TabBoxClientImpl *>(c)->client(); 0319 client->cancelShadeHoverTimer(); // stop core shading action 0320 if (!b && client->shadeMode() == ShadeNormal) { 0321 client->setShade(ShadeHover); 0322 } else if (b && client->shadeMode() == ShadeHover) { 0323 client->setShade(ShadeNormal); 0324 } 0325 } 0326 0327 QWeakPointer<TabBoxClient> TabBoxHandlerImpl::desktopClient() const 0328 { 0329 const auto stackingOrder = Workspace::self()->stackingOrder(); 0330 for (Window *window : stackingOrder) { 0331 if (window->isClient() && window->isDesktop() && window->isOnCurrentDesktop() && window->output() == workspace()->activeOutput()) { 0332 return qWeakPointerCast<TabBoxClient, TabBoxClientImpl>(window->tabBoxClient()); 0333 } 0334 } 0335 return QWeakPointer<TabBoxClient>(); 0336 } 0337 0338 void TabBoxHandlerImpl::activateAndClose() 0339 { 0340 m_tabBox->accept(); 0341 } 0342 0343 void TabBoxHandlerImpl::highlightWindows(TabBoxClient *window, QWindow *controller) 0344 { 0345 if (!effects) { 0346 return; 0347 } 0348 QVector<EffectWindow *> windows; 0349 if (window) { 0350 windows << static_cast<TabBoxClientImpl *>(window)->client()->effectWindow(); 0351 } 0352 if (Window *t = workspace()->findInternal(controller)) { 0353 windows << t->effectWindow(); 0354 } 0355 static_cast<EffectsHandlerImpl *>(effects)->highlightWindows(windows); 0356 } 0357 0358 bool TabBoxHandlerImpl::noModifierGrab() const 0359 { 0360 return m_tabBox->noModifierGrab(); 0361 } 0362 0363 /********************************************************* 0364 * TabBoxClientImpl 0365 *********************************************************/ 0366 0367 TabBoxClientImpl::TabBoxClientImpl(Window *client) 0368 : TabBoxClient() 0369 , m_client(client) 0370 { 0371 } 0372 0373 TabBoxClientImpl::~TabBoxClientImpl() 0374 { 0375 } 0376 0377 QString TabBoxClientImpl::caption() const 0378 { 0379 if (m_client->isDesktop()) { 0380 return i18nc("Special entry in alt+tab list for minimizing all windows", 0381 "Show Desktop"); 0382 } 0383 return m_client->caption(); 0384 } 0385 0386 QIcon TabBoxClientImpl::icon() const 0387 { 0388 if (m_client->isDesktop()) { 0389 return QIcon::fromTheme(QStringLiteral("user-desktop")); 0390 } 0391 return m_client->icon(); 0392 } 0393 0394 bool TabBoxClientImpl::isMinimized() const 0395 { 0396 return m_client->isMinimized(); 0397 } 0398 0399 int TabBoxClientImpl::x() const 0400 { 0401 return m_client->x(); 0402 } 0403 0404 int TabBoxClientImpl::y() const 0405 { 0406 return m_client->y(); 0407 } 0408 0409 int TabBoxClientImpl::width() const 0410 { 0411 return m_client->width(); 0412 } 0413 0414 int TabBoxClientImpl::height() const 0415 { 0416 return m_client->height(); 0417 } 0418 0419 bool TabBoxClientImpl::isCloseable() const 0420 { 0421 return m_client->isCloseable(); 0422 } 0423 0424 void TabBoxClientImpl::close() 0425 { 0426 m_client->closeWindow(); 0427 } 0428 0429 bool TabBoxClientImpl::isFirstInTabBox() const 0430 { 0431 return m_client->isFirstInTabBox(); 0432 } 0433 0434 QUuid TabBoxClientImpl::internalId() const 0435 { 0436 return m_client->internalId(); 0437 } 0438 0439 /********************************************************* 0440 * TabBox 0441 *********************************************************/ 0442 0443 TabBox::TabBox() 0444 : m_displayRefcount(0) 0445 , m_desktopGrab(false) 0446 , m_tabGrab(false) 0447 , m_noModifierGrab(false) 0448 , m_forcedGlobalMouseGrab(false) 0449 , m_ready(false) 0450 { 0451 m_isShown = false; 0452 m_defaultConfig = TabBoxConfig(); 0453 m_defaultConfig.setTabBoxMode(TabBoxConfig::ClientTabBox); 0454 m_defaultConfig.setClientDesktopMode(TabBoxConfig::OnlyCurrentDesktopClients); 0455 m_defaultConfig.setClientActivitiesMode(TabBoxConfig::OnlyCurrentActivityClients); 0456 m_defaultConfig.setClientApplicationsMode(TabBoxConfig::AllWindowsAllApplications); 0457 m_defaultConfig.setOrderMinimizedMode(TabBoxConfig::NoGroupByMinimized); 0458 m_defaultConfig.setClientMinimizedMode(TabBoxConfig::IgnoreMinimizedStatus); 0459 m_defaultConfig.setShowDesktopMode(TabBoxConfig::DoNotShowDesktopClient); 0460 m_defaultConfig.setClientMultiScreenMode(TabBoxConfig::IgnoreMultiScreen); 0461 m_defaultConfig.setClientSwitchingMode(TabBoxConfig::FocusChainSwitching); 0462 0463 m_alternativeConfig = TabBoxConfig(); 0464 m_alternativeConfig.setTabBoxMode(TabBoxConfig::ClientTabBox); 0465 m_alternativeConfig.setClientDesktopMode(TabBoxConfig::AllDesktopsClients); 0466 m_alternativeConfig.setClientActivitiesMode(TabBoxConfig::OnlyCurrentActivityClients); 0467 m_alternativeConfig.setClientApplicationsMode(TabBoxConfig::AllWindowsAllApplications); 0468 m_alternativeConfig.setOrderMinimizedMode(TabBoxConfig::NoGroupByMinimized); 0469 m_alternativeConfig.setClientMinimizedMode(TabBoxConfig::IgnoreMinimizedStatus); 0470 m_alternativeConfig.setShowDesktopMode(TabBoxConfig::DoNotShowDesktopClient); 0471 m_alternativeConfig.setClientMultiScreenMode(TabBoxConfig::IgnoreMultiScreen); 0472 m_alternativeConfig.setClientSwitchingMode(TabBoxConfig::FocusChainSwitching); 0473 0474 m_defaultCurrentApplicationConfig = m_defaultConfig; 0475 m_defaultCurrentApplicationConfig.setClientApplicationsMode(TabBoxConfig::AllWindowsCurrentApplication); 0476 0477 m_alternativeCurrentApplicationConfig = m_alternativeConfig; 0478 m_alternativeCurrentApplicationConfig.setClientApplicationsMode(TabBoxConfig::AllWindowsCurrentApplication); 0479 0480 m_desktopConfig = TabBoxConfig(); 0481 m_desktopConfig.setTabBoxMode(TabBoxConfig::DesktopTabBox); 0482 m_desktopConfig.setShowTabBox(true); 0483 m_desktopConfig.setShowDesktopMode(TabBoxConfig::DoNotShowDesktopClient); 0484 m_desktopConfig.setDesktopSwitchingMode(TabBoxConfig::MostRecentlyUsedDesktopSwitching); 0485 0486 m_desktopListConfig = TabBoxConfig(); 0487 m_desktopListConfig.setTabBoxMode(TabBoxConfig::DesktopTabBox); 0488 m_desktopListConfig.setShowTabBox(true); 0489 m_desktopListConfig.setShowDesktopMode(TabBoxConfig::DoNotShowDesktopClient); 0490 m_desktopListConfig.setDesktopSwitchingMode(TabBoxConfig::StaticDesktopSwitching); 0491 m_tabBox = new TabBoxHandlerImpl(this); 0492 QTimer::singleShot(0, this, &TabBox::handlerReady); 0493 0494 m_tabBoxMode = TabBoxDesktopMode; // init variables 0495 connect(&m_delayedShowTimer, &QTimer::timeout, this, &TabBox::show); 0496 connect(Workspace::self(), &Workspace::configChanged, this, &TabBox::reconfigure); 0497 } 0498 0499 TabBox::~TabBox() = default; 0500 0501 void TabBox::handlerReady() 0502 { 0503 m_tabBox->setConfig(m_defaultConfig); 0504 reconfigure(); 0505 m_ready = true; 0506 } 0507 0508 template<typename Slot> 0509 void TabBox::key(const KLazyLocalizedString &actionName, Slot slot, const QKeySequence &shortcut) 0510 { 0511 QAction *a = new QAction(this); 0512 a->setProperty("componentName", QStringLiteral("kwin")); 0513 a->setObjectName(QString::fromUtf8(actionName.untranslatedText())); 0514 a->setText(actionName.toString()); 0515 KGlobalAccel::self()->setGlobalShortcut(a, QList<QKeySequence>() << shortcut); 0516 connect(a, &QAction::triggered, this, slot); 0517 auto cuts = KGlobalAccel::self()->shortcut(a); 0518 globalShortcutChanged(a, cuts.isEmpty() ? QKeySequence() : cuts.first()); 0519 } 0520 0521 static constexpr const auto s_windows = kli18n("Walk Through Windows"); 0522 static constexpr const auto s_windowsRev = kli18n("Walk Through Windows (Reverse)"); 0523 static constexpr const auto s_windowsAlt = kli18n("Walk Through Windows Alternative"); 0524 static constexpr const auto s_windowsAltRev = kli18n("Walk Through Windows Alternative (Reverse)"); 0525 static constexpr const auto s_app = kli18n("Walk Through Windows of Current Application"); 0526 static constexpr const auto s_appRev = kli18n("Walk Through Windows of Current Application (Reverse)"); 0527 static constexpr const auto s_appAlt = kli18n("Walk Through Windows of Current Application Alternative"); 0528 static constexpr const auto s_appAltRev = kli18n("Walk Through Windows of Current Application Alternative (Reverse)"); 0529 static constexpr const auto s_desktops = kli18n("Walk Through Desktops"); 0530 static constexpr const auto s_desktopsRev = kli18n("Walk Through Desktops (Reverse)"); 0531 static constexpr const auto s_desktopList = kli18n("Walk Through Desktop List"); 0532 static constexpr const auto s_desktopListRev = kli18n("Walk Through Desktop List (Reverse)"); 0533 0534 void TabBox::initShortcuts() 0535 { 0536 key(s_windows, &TabBox::slotWalkThroughWindows, Qt::ALT | Qt::Key_Tab); 0537 key(s_windowsRev, &TabBox::slotWalkBackThroughWindows, Qt::ALT | Qt::SHIFT | Qt::Key_Backtab); 0538 key(s_app, &TabBox::slotWalkThroughCurrentAppWindows, Qt::ALT | Qt::Key_QuoteLeft); 0539 key(s_appRev, &TabBox::slotWalkBackThroughCurrentAppWindows, Qt::ALT | Qt::Key_AsciiTilde); 0540 key(s_windowsAlt, &TabBox::slotWalkThroughWindowsAlternative); 0541 key(s_windowsAltRev, &TabBox::slotWalkBackThroughWindowsAlternative); 0542 key(s_appAlt, &TabBox::slotWalkThroughCurrentAppWindowsAlternative); 0543 key(s_appAltRev, &TabBox::slotWalkBackThroughCurrentAppWindowsAlternative); 0544 key(s_desktops, &TabBox::slotWalkThroughDesktops); 0545 key(s_desktopsRev, &TabBox::slotWalkBackThroughDesktops); 0546 key(s_desktopList, &TabBox::slotWalkThroughDesktopList); 0547 key(s_desktopListRev, &TabBox::slotWalkBackThroughDesktopList); 0548 0549 connect(KGlobalAccel::self(), &KGlobalAccel::globalShortcutChanged, this, &TabBox::globalShortcutChanged); 0550 } 0551 0552 void TabBox::globalShortcutChanged(QAction *action, const QKeySequence &seq) 0553 { 0554 if (qstrcmp(qPrintable(action->objectName()), s_windows.untranslatedText()) == 0) { 0555 m_cutWalkThroughWindows = seq; 0556 } else if (qstrcmp(qPrintable(action->objectName()), s_windowsRev.untranslatedText()) == 0) { 0557 m_cutWalkThroughWindowsReverse = seq; 0558 } else if (qstrcmp(qPrintable(action->objectName()), s_app.untranslatedText()) == 0) { 0559 m_cutWalkThroughCurrentAppWindows = seq; 0560 } else if (qstrcmp(qPrintable(action->objectName()), s_appRev.untranslatedText()) == 0) { 0561 m_cutWalkThroughCurrentAppWindowsReverse = seq; 0562 } else if (qstrcmp(qPrintable(action->objectName()), s_windowsAlt.untranslatedText()) == 0) { 0563 m_cutWalkThroughWindowsAlternative = seq; 0564 } else if (qstrcmp(qPrintable(action->objectName()), s_windowsAltRev.untranslatedText()) == 0) { 0565 m_cutWalkThroughWindowsAlternativeReverse = seq; 0566 } else if (qstrcmp(qPrintable(action->objectName()), s_appAlt.untranslatedText()) == 0) { 0567 m_cutWalkThroughCurrentAppWindowsAlternative = seq; 0568 } else if (qstrcmp(qPrintable(action->objectName()), s_appAltRev.untranslatedText()) == 0) { 0569 m_cutWalkThroughCurrentAppWindowsAlternativeReverse = seq; 0570 } else if (qstrcmp(qPrintable(action->objectName()), s_desktops.untranslatedText()) == 0) { 0571 m_cutWalkThroughDesktops = seq; 0572 } else if (qstrcmp(qPrintable(action->objectName()), s_desktopsRev.untranslatedText()) == 0) { 0573 m_cutWalkThroughDesktopsReverse = seq; 0574 } else if (qstrcmp(qPrintable(action->objectName()), s_desktopList.untranslatedText()) == 0) { 0575 m_cutWalkThroughDesktopList = seq; 0576 } else if (qstrcmp(qPrintable(action->objectName()), s_desktopListRev.untranslatedText()) == 0) { 0577 m_cutWalkThroughDesktopListReverse = seq; 0578 } 0579 } 0580 0581 void TabBox::setMode(TabBoxMode mode) 0582 { 0583 m_tabBoxMode = mode; 0584 switch (mode) { 0585 case TabBoxWindowsMode: 0586 m_tabBox->setConfig(m_defaultConfig); 0587 break; 0588 case TabBoxWindowsAlternativeMode: 0589 m_tabBox->setConfig(m_alternativeConfig); 0590 break; 0591 case TabBoxCurrentAppWindowsMode: 0592 m_tabBox->setConfig(m_defaultCurrentApplicationConfig); 0593 break; 0594 case TabBoxCurrentAppWindowsAlternativeMode: 0595 m_tabBox->setConfig(m_alternativeCurrentApplicationConfig); 0596 break; 0597 case TabBoxDesktopMode: 0598 m_tabBox->setConfig(m_desktopConfig); 0599 break; 0600 case TabBoxDesktopListMode: 0601 m_tabBox->setConfig(m_desktopListConfig); 0602 break; 0603 } 0604 } 0605 0606 void TabBox::reset(bool partial_reset) 0607 { 0608 switch (m_tabBox->config().tabBoxMode()) { 0609 case TabBoxConfig::ClientTabBox: 0610 m_tabBox->createModel(partial_reset); 0611 if (!partial_reset) { 0612 if (Workspace::self()->activeWindow()) { 0613 setCurrentClient(Workspace::self()->activeWindow()); 0614 } 0615 // it's possible that the active client is not part of the model 0616 // in that case the index is invalid 0617 if (!m_tabBox->currentIndex().isValid()) { 0618 setCurrentIndex(m_tabBox->first()); 0619 } 0620 } else { 0621 if (!m_tabBox->currentIndex().isValid() || !m_tabBox->client(m_tabBox->currentIndex())) { 0622 setCurrentIndex(m_tabBox->first()); 0623 } 0624 } 0625 break; 0626 case TabBoxConfig::DesktopTabBox: 0627 m_tabBox->createModel(); 0628 0629 if (!partial_reset) { 0630 setCurrentDesktop(VirtualDesktopManager::self()->current()); 0631 } 0632 break; 0633 } 0634 0635 Q_EMIT tabBoxUpdated(); 0636 } 0637 0638 void TabBox::nextPrev(bool next) 0639 { 0640 setCurrentIndex(m_tabBox->nextPrev(next), false); 0641 Q_EMIT tabBoxUpdated(); 0642 } 0643 0644 Window *TabBox::currentClient() 0645 { 0646 if (TabBoxClientImpl *client = static_cast<TabBoxClientImpl *>(m_tabBox->client(m_tabBox->currentIndex()))) { 0647 if (!Workspace::self()->hasWindow(client->client())) { 0648 return nullptr; 0649 } 0650 return client->client(); 0651 } else { 0652 return nullptr; 0653 } 0654 } 0655 0656 QList<Window *> TabBox::currentClientList() 0657 { 0658 const TabBoxClientList list = m_tabBox->clientList(); 0659 QList<Window *> ret; 0660 for (const QWeakPointer<TabBoxClient> &clientPointer : list) { 0661 QSharedPointer<TabBoxClient> client = clientPointer.toStrongRef(); 0662 if (!client) { 0663 continue; 0664 } 0665 if (const TabBoxClientImpl *c = static_cast<const TabBoxClientImpl *>(client.data())) { 0666 ret.append(c->client()); 0667 } 0668 } 0669 return ret; 0670 } 0671 0672 int TabBox::currentDesktop() 0673 { 0674 return m_tabBox->desktop(m_tabBox->currentIndex()); 0675 } 0676 0677 QList<int> TabBox::currentDesktopList() 0678 { 0679 return m_tabBox->desktopList(); 0680 } 0681 0682 void TabBox::setCurrentClient(Window *newClient) 0683 { 0684 setCurrentIndex(m_tabBox->index(qWeakPointerCast<TabBoxClient, TabBoxClientImpl>(newClient->tabBoxClient()))); 0685 } 0686 0687 void TabBox::setCurrentDesktop(int newDesktop) 0688 { 0689 setCurrentIndex(m_tabBox->desktopIndex(newDesktop)); 0690 } 0691 0692 void TabBox::setCurrentIndex(QModelIndex index, bool notifyEffects) 0693 { 0694 if (!index.isValid()) { 0695 return; 0696 } 0697 m_tabBox->setCurrentIndex(index); 0698 if (notifyEffects) { 0699 Q_EMIT tabBoxUpdated(); 0700 } 0701 } 0702 0703 void TabBox::show() 0704 { 0705 Q_EMIT tabBoxAdded(m_tabBoxMode); 0706 if (isDisplayed()) { 0707 m_isShown = false; 0708 return; 0709 } 0710 workspace()->setShowingDesktop(false); 0711 reference(); 0712 m_isShown = true; 0713 m_tabBox->show(); 0714 } 0715 0716 void TabBox::hide(bool abort) 0717 { 0718 m_delayedShowTimer.stop(); 0719 if (m_isShown) { 0720 m_isShown = false; 0721 unreference(); 0722 } 0723 Q_EMIT tabBoxClosed(); 0724 if (isDisplayed()) { 0725 qCDebug(KWIN_TABBOX) << "Tab box was not properly closed by an effect"; 0726 } 0727 m_tabBox->hide(abort); 0728 } 0729 0730 void TabBox::reconfigure() 0731 { 0732 KSharedConfigPtr c = kwinApp()->config(); 0733 KConfigGroup config = c->group("TabBox"); 0734 0735 loadConfig(c->group("TabBox"), m_defaultConfig); 0736 loadConfig(c->group("TabBoxAlternative"), m_alternativeConfig); 0737 0738 m_defaultCurrentApplicationConfig = m_defaultConfig; 0739 m_defaultCurrentApplicationConfig.setClientApplicationsMode(TabBoxConfig::AllWindowsCurrentApplication); 0740 m_alternativeCurrentApplicationConfig = m_alternativeConfig; 0741 m_alternativeCurrentApplicationConfig.setClientApplicationsMode(TabBoxConfig::AllWindowsCurrentApplication); 0742 0743 m_tabBox->setConfig(m_defaultConfig); 0744 0745 m_delayShowTime = config.readEntry<int>("DelayTime", 90); 0746 0747 const QString defaultDesktopLayout = QStringLiteral("org.kde.breeze.desktop"); 0748 m_desktopConfig.setLayoutName(config.readEntry("DesktopLayout", defaultDesktopLayout)); 0749 m_desktopListConfig.setLayoutName(config.readEntry("DesktopListLayout", defaultDesktopLayout)); 0750 0751 QList<ElectricBorder> *borders = &m_borderActivate; 0752 QString borderConfig = QStringLiteral("BorderActivate"); 0753 for (int i = 0; i < 2; ++i) { 0754 for (ElectricBorder border : std::as_const(*borders)) { 0755 workspace()->screenEdges()->unreserve(border, this); 0756 } 0757 borders->clear(); 0758 QStringList list = config.readEntry(borderConfig, QStringList()); 0759 for (const QString &s : std::as_const(list)) { 0760 bool ok; 0761 const int i = s.toInt(&ok); 0762 if (!ok) { 0763 continue; 0764 } 0765 borders->append(ElectricBorder(i)); 0766 workspace()->screenEdges()->reserve(ElectricBorder(i), this, "toggle"); 0767 } 0768 borders = &m_borderAlternativeActivate; 0769 borderConfig = QStringLiteral("BorderAlternativeActivate"); 0770 } 0771 0772 auto touchConfig = [this, config](const QString &key, QHash<ElectricBorder, QAction *> &actions, TabBoxMode mode, const QStringList &defaults = QStringList{}) { 0773 // fist erase old config 0774 for (auto it = actions.begin(); it != actions.end();) { 0775 delete it.value(); 0776 it = actions.erase(it); 0777 } 0778 // now new config 0779 const QStringList list = config.readEntry(key, defaults); 0780 for (const auto &s : list) { 0781 bool ok; 0782 const int i = s.toInt(&ok); 0783 if (!ok) { 0784 continue; 0785 } 0786 QAction *a = new QAction(this); 0787 connect(a, &QAction::triggered, this, std::bind(&TabBox::toggleMode, this, mode)); 0788 workspace()->screenEdges()->reserveTouch(ElectricBorder(i), a); 0789 actions.insert(ElectricBorder(i), a); 0790 } 0791 }; 0792 touchConfig(QStringLiteral("TouchBorderActivate"), m_touchActivate, TabBoxWindowsMode); 0793 touchConfig(QStringLiteral("TouchBorderAlternativeActivate"), m_touchAlternativeActivate, TabBoxWindowsAlternativeMode); 0794 } 0795 0796 void TabBox::loadConfig(const KConfigGroup &config, TabBoxConfig &tabBoxConfig) 0797 { 0798 tabBoxConfig.setClientDesktopMode(TabBoxConfig::ClientDesktopMode( 0799 config.readEntry<int>("DesktopMode", TabBoxConfig::defaultDesktopMode()))); 0800 tabBoxConfig.setClientActivitiesMode(TabBoxConfig::ClientActivitiesMode( 0801 config.readEntry<int>("ActivitiesMode", TabBoxConfig::defaultActivitiesMode()))); 0802 tabBoxConfig.setClientApplicationsMode(TabBoxConfig::ClientApplicationsMode( 0803 config.readEntry<int>("ApplicationsMode", TabBoxConfig::defaultApplicationsMode()))); 0804 tabBoxConfig.setOrderMinimizedMode(TabBoxConfig::OrderMinimizedMode( 0805 config.readEntry<int>("OrderMinimizedMode", TabBoxConfig::defaultOrderMinimizedMode()))); 0806 tabBoxConfig.setClientMinimizedMode(TabBoxConfig::ClientMinimizedMode( 0807 config.readEntry<int>("MinimizedMode", TabBoxConfig::defaultMinimizedMode()))); 0808 tabBoxConfig.setShowDesktopMode(TabBoxConfig::ShowDesktopMode( 0809 config.readEntry<int>("ShowDesktopMode", TabBoxConfig::defaultShowDesktopMode()))); 0810 tabBoxConfig.setClientMultiScreenMode(TabBoxConfig::ClientMultiScreenMode( 0811 config.readEntry<int>("MultiScreenMode", TabBoxConfig::defaultMultiScreenMode()))); 0812 tabBoxConfig.setClientSwitchingMode(TabBoxConfig::ClientSwitchingMode( 0813 config.readEntry<int>("SwitchingMode", TabBoxConfig::defaultSwitchingMode()))); 0814 0815 tabBoxConfig.setShowTabBox(config.readEntry<bool>("ShowTabBox", 0816 TabBoxConfig::defaultShowTabBox())); 0817 tabBoxConfig.setHighlightWindows(config.readEntry<bool>("HighlightWindows", 0818 TabBoxConfig::defaultHighlightWindow())); 0819 0820 tabBoxConfig.setLayoutName(config.readEntry<QString>("LayoutName", TabBoxConfig::defaultLayoutName())); 0821 } 0822 0823 void TabBox::delayedShow() 0824 { 0825 if (isDisplayed() || m_delayedShowTimer.isActive()) { 0826 // already called show - no need to call it twice 0827 return; 0828 } 0829 0830 if (!m_delayShowTime) { 0831 show(); 0832 return; 0833 } 0834 0835 m_delayedShowTimer.setSingleShot(true); 0836 m_delayedShowTimer.start(m_delayShowTime); 0837 } 0838 0839 bool TabBox::handleMouseEvent(QMouseEvent *event) 0840 { 0841 if (!m_isShown && isDisplayed()) { 0842 // tabbox has been replaced, check effects 0843 if (effects && static_cast<EffectsHandlerImpl *>(effects)->checkInputWindowEvent(event)) { 0844 return true; 0845 } 0846 } 0847 switch (event->type()) { 0848 case QEvent::MouseMove: 0849 if (!m_tabBox->containsPos(event->globalPos())) { 0850 // filter out all events which are not on the TabBox window. 0851 // We don't want windows to react on the mouse events 0852 return true; 0853 } 0854 return false; 0855 case QEvent::MouseButtonPress: 0856 if ((!m_isShown && isDisplayed()) || !m_tabBox->containsPos(event->globalPos())) { 0857 close(); // click outside closes tab 0858 return true; 0859 } 0860 // fall through 0861 case QEvent::MouseButtonRelease: 0862 default: 0863 // we do not filter it out, the intenal filter takes care 0864 return false; 0865 } 0866 return false; 0867 } 0868 0869 bool TabBox::handleWheelEvent(QWheelEvent *event) 0870 { 0871 if (!m_isShown && isDisplayed()) { 0872 // tabbox has been replaced, check effects 0873 if (effects && static_cast<EffectsHandlerImpl *>(effects)->checkInputWindowEvent(event)) { 0874 return true; 0875 } 0876 } 0877 if (event->angleDelta().y() == 0) { 0878 return false; 0879 } 0880 const QModelIndex index = m_tabBox->nextPrev(event->angleDelta().y() > 0); 0881 if (index.isValid()) { 0882 setCurrentIndex(index); 0883 } 0884 return true; 0885 } 0886 0887 void TabBox::grabbedKeyEvent(QKeyEvent *event) 0888 { 0889 Q_EMIT tabBoxKeyEvent(event); 0890 if (!m_isShown && isDisplayed()) { 0891 // tabbox has been replaced, check effects 0892 return; 0893 } 0894 if (m_noModifierGrab) { 0895 if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return || event->key() == Qt::Key_Space) { 0896 accept(); 0897 return; 0898 } 0899 } 0900 m_tabBox->grabbedKeyEvent(event); 0901 } 0902 0903 struct KeySymbolsDeleter 0904 { 0905 void operator()(xcb_key_symbols_t *symbols) 0906 { 0907 xcb_key_symbols_free(symbols); 0908 } 0909 }; 0910 0911 /** 0912 * Handles alt-tab / control-tab 0913 */ 0914 static bool areKeySymXsDepressed(const uint keySyms[], int nKeySyms) 0915 { 0916 Xcb::QueryKeymap keys; 0917 0918 std::unique_ptr<xcb_key_symbols_t, KeySymbolsDeleter> symbols(xcb_key_symbols_alloc(connection())); 0919 if (!symbols || !keys) { 0920 return false; 0921 } 0922 const auto keymap = keys->keys; 0923 0924 bool depressed = false; 0925 for (int iKeySym = 0; iKeySym < nKeySyms; iKeySym++) { 0926 uint keySymX = keySyms[iKeySym]; 0927 xcb_keycode_t *keyCodes = xcb_key_symbols_get_keycode(symbols.get(), keySymX); 0928 if (!keyCodes) { 0929 continue; 0930 } 0931 0932 int j = 0; 0933 while (keyCodes[j] != XCB_NO_SYMBOL) { 0934 const xcb_keycode_t keyCodeX = keyCodes[j++]; 0935 int i = keyCodeX / 8; 0936 char mask = 1 << (keyCodeX - (i * 8)); 0937 0938 if (i < 0 || i >= 32) { 0939 continue; 0940 } 0941 0942 qCDebug(KWIN_TABBOX) << iKeySym << ": keySymX=0x" << QString::number(keySymX, 16) 0943 << " i=" << i << " mask=0x" << QString::number(mask, 16) 0944 << " keymap[i]=0x" << QString::number(keymap[i], 16); 0945 0946 if (keymap[i] & mask) { 0947 depressed = true; 0948 break; 0949 } 0950 } 0951 0952 free(keyCodes); 0953 } 0954 0955 return depressed; 0956 } 0957 0958 static bool areModKeysDepressedX11(const QKeySequence &seq) 0959 { 0960 uint rgKeySyms[10]; 0961 int nKeySyms = 0; 0962 int mod = seq[seq.count() - 1] & Qt::KeyboardModifierMask; 0963 0964 if (mod & Qt::SHIFT) { 0965 rgKeySyms[nKeySyms++] = XK_Shift_L; 0966 rgKeySyms[nKeySyms++] = XK_Shift_R; 0967 } 0968 if (mod & Qt::CTRL) { 0969 rgKeySyms[nKeySyms++] = XK_Control_L; 0970 rgKeySyms[nKeySyms++] = XK_Control_R; 0971 } 0972 if (mod & Qt::ALT) { 0973 rgKeySyms[nKeySyms++] = XK_Alt_L; 0974 rgKeySyms[nKeySyms++] = XK_Alt_R; 0975 } 0976 if (mod & Qt::META) { 0977 // It would take some code to determine whether the Win key 0978 // is associated with Super or Meta, so check for both. 0979 // See bug #140023 for details. 0980 rgKeySyms[nKeySyms++] = XK_Super_L; 0981 rgKeySyms[nKeySyms++] = XK_Super_R; 0982 rgKeySyms[nKeySyms++] = XK_Meta_L; 0983 rgKeySyms[nKeySyms++] = XK_Meta_R; 0984 } 0985 0986 return areKeySymXsDepressed(rgKeySyms, nKeySyms); 0987 } 0988 0989 static bool areModKeysDepressedWayland(const QKeySequence &seq) 0990 { 0991 const int mod = seq[seq.count() - 1] & Qt::KeyboardModifierMask; 0992 const Qt::KeyboardModifiers mods = input()->modifiersRelevantForGlobalShortcuts(); 0993 if ((mod & Qt::SHIFT) && mods.testFlag(Qt::ShiftModifier)) { 0994 return true; 0995 } 0996 if ((mod & Qt::CTRL) && mods.testFlag(Qt::ControlModifier)) { 0997 return true; 0998 } 0999 if ((mod & Qt::ALT) && mods.testFlag(Qt::AltModifier)) { 1000 return true; 1001 } 1002 if ((mod & Qt::META) && mods.testFlag(Qt::MetaModifier)) { 1003 return true; 1004 } 1005 return false; 1006 } 1007 1008 static bool areModKeysDepressed(const QKeySequence &seq) 1009 { 1010 if (seq.isEmpty()) { 1011 return false; 1012 } 1013 if (kwinApp()->shouldUseWaylandForCompositing()) { 1014 return areModKeysDepressedWayland(seq); 1015 } else { 1016 return areModKeysDepressedX11(seq); 1017 } 1018 } 1019 1020 void TabBox::navigatingThroughWindows(bool forward, const QKeySequence &shortcut, TabBoxMode mode) 1021 { 1022 if (!m_ready || isGrabbed()) { 1023 return; 1024 } 1025 if (!options->focusPolicyIsReasonable()) { 1026 // ungrabXKeyboard(); // need that because of accelerator raw mode 1027 // CDE style raise / lower 1028 CDEWalkThroughWindows(forward); 1029 } else { 1030 if (areModKeysDepressed(shortcut)) { 1031 if (startKDEWalkThroughWindows(mode)) { 1032 KDEWalkThroughWindows(forward); 1033 } 1034 } else { 1035 // if the shortcut has no modifiers, don't show the tabbox, 1036 // don't grab, but simply go to the next window 1037 KDEOneStepThroughWindows(forward, mode); 1038 } 1039 } 1040 } 1041 1042 void TabBox::slotWalkThroughWindows() 1043 { 1044 navigatingThroughWindows(true, m_cutWalkThroughWindows, TabBoxWindowsMode); 1045 } 1046 1047 void TabBox::slotWalkBackThroughWindows() 1048 { 1049 navigatingThroughWindows(false, m_cutWalkThroughWindowsReverse, TabBoxWindowsMode); 1050 } 1051 1052 void TabBox::slotWalkThroughWindowsAlternative() 1053 { 1054 navigatingThroughWindows(true, m_cutWalkThroughWindowsAlternative, TabBoxWindowsAlternativeMode); 1055 } 1056 1057 void TabBox::slotWalkBackThroughWindowsAlternative() 1058 { 1059 navigatingThroughWindows(false, m_cutWalkThroughWindowsAlternativeReverse, TabBoxWindowsAlternativeMode); 1060 } 1061 1062 void TabBox::slotWalkThroughCurrentAppWindows() 1063 { 1064 navigatingThroughWindows(true, m_cutWalkThroughCurrentAppWindows, TabBoxCurrentAppWindowsMode); 1065 } 1066 1067 void TabBox::slotWalkBackThroughCurrentAppWindows() 1068 { 1069 navigatingThroughWindows(false, m_cutWalkThroughCurrentAppWindowsReverse, TabBoxCurrentAppWindowsMode); 1070 } 1071 1072 void TabBox::slotWalkThroughCurrentAppWindowsAlternative() 1073 { 1074 navigatingThroughWindows(true, m_cutWalkThroughCurrentAppWindowsAlternative, TabBoxCurrentAppWindowsAlternativeMode); 1075 } 1076 1077 void TabBox::slotWalkBackThroughCurrentAppWindowsAlternative() 1078 { 1079 navigatingThroughWindows(false, m_cutWalkThroughCurrentAppWindowsAlternativeReverse, TabBoxCurrentAppWindowsAlternativeMode); 1080 } 1081 1082 void TabBox::slotWalkThroughDesktops() 1083 { 1084 if (!m_ready || isGrabbed()) { 1085 return; 1086 } 1087 if (areModKeysDepressed(m_cutWalkThroughDesktops)) { 1088 if (startWalkThroughDesktops()) { 1089 walkThroughDesktops(true); 1090 } 1091 } else { 1092 oneStepThroughDesktops(true); 1093 } 1094 } 1095 1096 void TabBox::slotWalkBackThroughDesktops() 1097 { 1098 if (!m_ready || isGrabbed()) { 1099 return; 1100 } 1101 if (areModKeysDepressed(m_cutWalkThroughDesktopsReverse)) { 1102 if (startWalkThroughDesktops()) { 1103 walkThroughDesktops(false); 1104 } 1105 } else { 1106 oneStepThroughDesktops(false); 1107 } 1108 } 1109 1110 void TabBox::slotWalkThroughDesktopList() 1111 { 1112 if (!m_ready || isGrabbed()) { 1113 return; 1114 } 1115 if (areModKeysDepressed(m_cutWalkThroughDesktopList)) { 1116 if (startWalkThroughDesktopList()) { 1117 walkThroughDesktops(true); 1118 } 1119 } else { 1120 oneStepThroughDesktopList(true); 1121 } 1122 } 1123 1124 void TabBox::slotWalkBackThroughDesktopList() 1125 { 1126 if (!m_ready || isGrabbed()) { 1127 return; 1128 } 1129 if (areModKeysDepressed(m_cutWalkThroughDesktopListReverse)) { 1130 if (startWalkThroughDesktopList()) { 1131 walkThroughDesktops(false); 1132 } 1133 } else { 1134 oneStepThroughDesktopList(false); 1135 } 1136 } 1137 1138 void TabBox::shadeActivate(Window *c) 1139 { 1140 if ((c->shadeMode() == ShadeNormal || c->shadeMode() == ShadeHover) && options->isShadeHover()) { 1141 c->setShade(ShadeActivated); 1142 } 1143 } 1144 1145 bool TabBox::toggle(ElectricBorder eb) 1146 { 1147 if (m_borderAlternativeActivate.contains(eb)) { 1148 return toggleMode(TabBoxWindowsAlternativeMode); 1149 } else { 1150 return toggleMode(TabBoxWindowsMode); 1151 } 1152 } 1153 1154 bool TabBox::toggleMode(TabBoxMode mode) 1155 { 1156 if (!options->focusPolicyIsReasonable()) { 1157 return false; // not supported. 1158 } 1159 if (isDisplayed()) { 1160 accept(); 1161 return true; 1162 } 1163 if (!establishTabBoxGrab()) { 1164 return false; 1165 } 1166 m_noModifierGrab = m_tabGrab = true; 1167 setMode(mode); 1168 reset(); 1169 show(); 1170 return true; 1171 } 1172 1173 bool TabBox::startKDEWalkThroughWindows(TabBoxMode mode) 1174 { 1175 if (!establishTabBoxGrab()) { 1176 return false; 1177 } 1178 m_tabGrab = true; 1179 m_noModifierGrab = false; 1180 setMode(mode); 1181 reset(); 1182 return true; 1183 } 1184 1185 bool TabBox::startWalkThroughDesktops(TabBoxMode mode) 1186 { 1187 if (!establishTabBoxGrab()) { 1188 return false; 1189 } 1190 m_desktopGrab = true; 1191 m_noModifierGrab = false; 1192 setMode(mode); 1193 reset(); 1194 return true; 1195 } 1196 1197 bool TabBox::startWalkThroughDesktops() 1198 { 1199 return startWalkThroughDesktops(TabBoxDesktopMode); 1200 } 1201 1202 bool TabBox::startWalkThroughDesktopList() 1203 { 1204 return startWalkThroughDesktops(TabBoxDesktopListMode); 1205 } 1206 1207 void TabBox::KDEWalkThroughWindows(bool forward) 1208 { 1209 nextPrev(forward); 1210 delayedShow(); 1211 } 1212 1213 void TabBox::walkThroughDesktops(bool forward) 1214 { 1215 nextPrev(forward); 1216 delayedShow(); 1217 } 1218 1219 void TabBox::CDEWalkThroughWindows(bool forward) 1220 { 1221 Window *c = nullptr; 1222 // this function find the first suitable client for unreasonable focus 1223 // policies - the topmost one, with some exceptions (can't be keepabove/below, 1224 // otherwise it gets stuck on them) 1225 // Q_ASSERT(Workspace::self()->block_stacking_updates == 0); 1226 for (int i = Workspace::self()->stackingOrder().size() - 1; i >= 0; --i) { 1227 auto t = Workspace::self()->stackingOrder().at(i); 1228 if (t->isClient() && t->isOnCurrentActivity() && t->isOnCurrentDesktop() && !t->isSpecialWindow() 1229 && !t->isShade() && t->isShown() && t->wantsTabFocus() 1230 && !t->keepAbove() && !t->keepBelow()) { 1231 c = t; 1232 break; 1233 } 1234 } 1235 Window *nc = c; 1236 bool options_traverse_all; 1237 { 1238 KConfigGroup group(kwinApp()->config(), "TabBox"); 1239 options_traverse_all = group.readEntry("TraverseAll", false); 1240 } 1241 1242 Window *firstClient = nullptr; 1243 do { 1244 nc = forward ? nextClientStatic(nc) : previousClientStatic(nc); 1245 if (!firstClient) { 1246 // When we see our first client for the second time, 1247 // it's time to stop. 1248 firstClient = nc; 1249 } else if (nc == firstClient) { 1250 // No candidates found. 1251 nc = nullptr; 1252 break; 1253 } 1254 } while (nc && nc != c && ((!options_traverse_all && !nc->isOnDesktop(currentDesktop())) || nc->isMinimized() || !nc->wantsTabFocus() || nc->keepAbove() || nc->keepBelow() || !nc->isOnCurrentActivity())); 1255 if (nc) { 1256 if (c && c != nc) { 1257 Workspace::self()->lowerWindow(c); 1258 } 1259 if (options->focusPolicyIsReasonable()) { 1260 Workspace::self()->activateWindow(nc); 1261 shadeActivate(nc); 1262 } else { 1263 if (!nc->isOnDesktop(currentDesktop())) { 1264 setCurrentDesktop(nc->desktop()); 1265 } 1266 Workspace::self()->raiseWindow(nc); 1267 } 1268 } 1269 } 1270 1271 void TabBox::KDEOneStepThroughWindows(bool forward, TabBoxMode mode) 1272 { 1273 setMode(mode); 1274 reset(); 1275 nextPrev(forward); 1276 if (Window *c = currentClient()) { 1277 Workspace::self()->activateWindow(c); 1278 shadeActivate(c); 1279 } 1280 } 1281 1282 void TabBox::oneStepThroughDesktops(bool forward, TabBoxMode mode) 1283 { 1284 setMode(mode); 1285 reset(); 1286 nextPrev(forward); 1287 if (currentDesktop() != -1) { 1288 setCurrentDesktop(currentDesktop()); 1289 } 1290 } 1291 1292 void TabBox::oneStepThroughDesktops(bool forward) 1293 { 1294 oneStepThroughDesktops(forward, TabBoxDesktopMode); 1295 } 1296 1297 void TabBox::oneStepThroughDesktopList(bool forward) 1298 { 1299 oneStepThroughDesktops(forward, TabBoxDesktopListMode); 1300 } 1301 1302 void TabBox::keyPress(int keyQt) 1303 { 1304 enum Direction { 1305 Backward = -1, 1306 Steady = 0, 1307 Forward = 1, 1308 }; 1309 Direction direction(Steady); 1310 1311 auto contains = [](const QKeySequence &shortcut, int key) -> bool { 1312 for (int i = 0; i < shortcut.count(); ++i) { 1313 if (shortcut[i] == key) { 1314 return true; 1315 } 1316 } 1317 return false; 1318 }; 1319 1320 // tests whether a shortcut matches and handles pitfalls on ShiftKey invocation 1321 auto directionFor = [keyQt, contains](const QKeySequence &forward, const QKeySequence &backward) -> Direction { 1322 if (contains(forward, keyQt)) { 1323 return Forward; 1324 } 1325 if (contains(backward, keyQt)) { 1326 return Backward; 1327 } 1328 if (!(keyQt & Qt::ShiftModifier)) { 1329 return Steady; 1330 } 1331 1332 // Before testing the unshifted key (Ctrl+A vs. Ctrl+Shift+a etc.), see whether this is +Shift+Tab 1333 // and check that against +Shift+Backtab (as well) 1334 Qt::KeyboardModifiers mods = Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier | Qt::KeypadModifier | Qt::GroupSwitchModifier; 1335 mods &= keyQt; 1336 if ((keyQt & ~mods) == Qt::Key_Tab) { 1337 if (contains(forward, mods | Qt::Key_Backtab)) { 1338 return Forward; 1339 } 1340 if (contains(backward, mods | Qt::Key_Backtab)) { 1341 return Backward; 1342 } 1343 } 1344 1345 // if the shortcuts do not match, try matching again after filtering the shift key from keyQt 1346 // it is needed to handle correctly the ALT+~ shorcut for example as it is coded as ALT+SHIFT+~ in keyQt 1347 if (contains(forward, keyQt & ~Qt::ShiftModifier)) { 1348 return Forward; 1349 } 1350 if (contains(backward, keyQt & ~Qt::ShiftModifier)) { 1351 return Backward; 1352 } 1353 1354 return Steady; 1355 }; 1356 1357 if (m_tabGrab) { 1358 static const int ModeCount = 4; 1359 static const TabBoxMode modes[ModeCount] = { 1360 TabBoxWindowsMode, TabBoxWindowsAlternativeMode, 1361 TabBoxCurrentAppWindowsMode, TabBoxCurrentAppWindowsAlternativeMode}; 1362 const QKeySequence cuts[2 * ModeCount] = { 1363 // forward 1364 m_cutWalkThroughWindows, m_cutWalkThroughWindowsAlternative, 1365 m_cutWalkThroughCurrentAppWindows, m_cutWalkThroughCurrentAppWindowsAlternative, 1366 // backward 1367 m_cutWalkThroughWindowsReverse, m_cutWalkThroughWindowsAlternativeReverse, 1368 m_cutWalkThroughCurrentAppWindowsReverse, m_cutWalkThroughCurrentAppWindowsAlternativeReverse}; 1369 bool testedCurrent = false; // in case of collision, prefer to stay in the current mode 1370 int i = 0, j = 0; 1371 while (true) { 1372 if (!testedCurrent && modes[i] != mode()) { 1373 ++j; 1374 i = (i + 1) % ModeCount; 1375 continue; 1376 } 1377 if (testedCurrent && modes[i] == mode()) { 1378 break; 1379 } 1380 testedCurrent = true; 1381 direction = directionFor(cuts[i], cuts[i + ModeCount]); 1382 if (direction != Steady) { 1383 if (modes[i] != mode()) { 1384 accept(false); 1385 setMode(modes[i]); 1386 auto replayWithChangedTabboxMode = [this, direction]() { 1387 reset(); 1388 nextPrev(direction == Forward); 1389 }; 1390 QTimer::singleShot(50, this, replayWithChangedTabboxMode); 1391 } 1392 break; 1393 } else if (++j > 2 * ModeCount) { // guarding counter for invalid modes 1394 qCDebug(KWIN_TABBOX) << "Invalid TabBoxMode"; 1395 return; 1396 } 1397 i = (i + 1) % ModeCount; 1398 } 1399 if (direction != Steady) { 1400 qCDebug(KWIN_TABBOX) << "== " << cuts[i].toString() << " or " << cuts[i + ModeCount].toString(); 1401 KDEWalkThroughWindows(direction == Forward); 1402 } 1403 } else if (m_desktopGrab) { 1404 direction = directionFor(m_cutWalkThroughDesktops, m_cutWalkThroughDesktopsReverse); 1405 if (direction == Steady) { 1406 direction = directionFor(m_cutWalkThroughDesktopList, m_cutWalkThroughDesktopListReverse); 1407 } 1408 if (direction != Steady) { 1409 walkThroughDesktops(direction == Forward); 1410 } 1411 } 1412 1413 if (m_desktopGrab || m_tabGrab) { 1414 if (((keyQt & ~Qt::KeyboardModifierMask) == Qt::Key_Escape) && direction == Steady) { 1415 // if Escape is part of the shortcut, don't cancel 1416 close(true); 1417 } else if (direction == Steady) { 1418 QKeyEvent event(QEvent::KeyPress, keyQt & ~Qt::KeyboardModifierMask, Qt::NoModifier); 1419 grabbedKeyEvent(&event); 1420 } 1421 } 1422 } 1423 1424 void TabBox::close(bool abort) 1425 { 1426 if (isGrabbed()) { 1427 removeTabBoxGrab(); 1428 } 1429 hide(abort); 1430 input()->pointer()->setEnableConstraints(true); 1431 m_tabGrab = false; 1432 m_desktopGrab = false; 1433 m_noModifierGrab = false; 1434 } 1435 1436 void TabBox::accept(bool closeTabBox) 1437 { 1438 Window *c = currentClient(); 1439 if (closeTabBox) { 1440 close(); 1441 } 1442 if (c) { 1443 Workspace::self()->activateWindow(c); 1444 shadeActivate(c); 1445 if (c->isDesktop()) { 1446 Workspace::self()->setShowingDesktop(!Workspace::self()->showingDesktop(), !m_desktopListConfig.isHighlightWindows()); 1447 } 1448 } 1449 } 1450 1451 void TabBox::modifiersReleased() 1452 { 1453 if (m_noModifierGrab) { 1454 return; 1455 } 1456 if (m_tabGrab) { 1457 bool old_control_grab = m_desktopGrab; 1458 accept(); 1459 m_desktopGrab = old_control_grab; 1460 } 1461 if (m_desktopGrab) { 1462 bool old_tab_grab = m_tabGrab; 1463 int desktop = currentDesktop(); 1464 close(); 1465 m_tabGrab = old_tab_grab; 1466 if (desktop != -1) { 1467 setCurrentDesktop(desktop); 1468 VirtualDesktopManager::self()->setCurrent(desktop); 1469 } 1470 } 1471 } 1472 1473 int TabBox::nextDesktopStatic(int iDesktop) const 1474 { 1475 return VirtualDesktopManager::self()->inDirection(iDesktop, VirtualDesktopManager::Direction::Next, true); 1476 } 1477 1478 int TabBox::previousDesktopStatic(int iDesktop) const 1479 { 1480 return VirtualDesktopManager::self()->inDirection(iDesktop, VirtualDesktopManager::Direction::Previous, true); 1481 } 1482 1483 /** 1484 * Auxiliary functions to travers all clients according to the static 1485 * order. Useful for the CDE-style Alt-tab feature. 1486 */ 1487 Window *TabBox::nextClientStatic(Window *c) const 1488 { 1489 const auto &list = Workspace::self()->allClientList(); 1490 if (!c || list.isEmpty()) { 1491 return nullptr; 1492 } 1493 int pos = list.indexOf(c); 1494 if (pos == -1) { 1495 return list.first(); 1496 } 1497 ++pos; 1498 if (pos == list.count()) { 1499 return list.first(); 1500 } 1501 return list.at(pos); 1502 } 1503 1504 /** 1505 * Auxiliary functions to travers all clients according to the static 1506 * order. Useful for the CDE-style Alt-tab feature. 1507 */ 1508 Window *TabBox::previousClientStatic(Window *c) const 1509 { 1510 const auto &list = Workspace::self()->allClientList(); 1511 if (!c || list.isEmpty()) { 1512 return nullptr; 1513 } 1514 int pos = list.indexOf(c); 1515 if (pos == -1) { 1516 return list.last(); 1517 } 1518 if (pos == 0) { 1519 return list.last(); 1520 } 1521 --pos; 1522 return list.at(pos); 1523 } 1524 1525 bool TabBox::establishTabBoxGrab() 1526 { 1527 if (kwinApp()->shouldUseWaylandForCompositing()) { 1528 m_forcedGlobalMouseGrab = true; 1529 return true; 1530 } 1531 kwinApp()->updateXTime(); 1532 if (!grabXKeyboard()) { 1533 return false; 1534 } 1535 // Don't try to establish a global mouse grab using XGrabPointer, as that would prevent 1536 // using Alt+Tab while DND (#44972). However force passive grabs on all windows 1537 // in order to catch MouseRelease events and close the tabbox (#67416). 1538 // All clients already have passive grabs in their wrapper windows, so check only 1539 // the active client, which may not have it. 1540 Q_ASSERT(!m_forcedGlobalMouseGrab); 1541 m_forcedGlobalMouseGrab = true; 1542 if (Workspace::self()->activeWindow() != nullptr) { 1543 Workspace::self()->activeWindow()->updateMouseGrab(); 1544 } 1545 m_x11EventFilter.reset(new X11Filter); 1546 return true; 1547 } 1548 1549 void TabBox::removeTabBoxGrab() 1550 { 1551 if (kwinApp()->shouldUseWaylandForCompositing()) { 1552 m_forcedGlobalMouseGrab = false; 1553 return; 1554 } 1555 kwinApp()->updateXTime(); 1556 ungrabXKeyboard(); 1557 Q_ASSERT(m_forcedGlobalMouseGrab); 1558 m_forcedGlobalMouseGrab = false; 1559 if (Workspace::self()->activeWindow() != nullptr) { 1560 Workspace::self()->activeWindow()->updateMouseGrab(); 1561 } 1562 m_x11EventFilter.reset(); 1563 } 1564 } // namespace TabBox 1565 } // namespace