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