File indexing completed on 2024-05-19 05:32:26

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/tabbox_logging.h"
0018 #include "tabbox/tabboxconfig.h"
0019 #include "tabbox/x11_filter.h"
0020 // kwin
0021 #if KWIN_BUILD_ACTIVITIES
0022 #include "activities.h"
0023 #endif
0024 #include "compositor.h"
0025 #include "effect/effecthandler.h"
0026 #include "focuschain.h"
0027 #include "input.h"
0028 #include "keyboard_input.h"
0029 #include "pointer_input.h"
0030 #include "screenedge.h"
0031 #include "utils/xcbutils.h"
0032 #include "virtualdesktops.h"
0033 #include "workspace.h"
0034 #include "x11window.h"
0035 // Qt
0036 #include <QAction>
0037 #include <QKeyEvent>
0038 // KDE
0039 #include <KConfig>
0040 #include <KConfigGroup>
0041 #include <KGlobalAccel>
0042 #include <KLazyLocalizedString>
0043 #include <KLocalizedString>
0044 #include <kkeyserver.h>
0045 // X11
0046 #include <X11/keysym.h>
0047 #include <X11/keysymdef.h>
0048 // xcb
0049 #include <xcb/xcb_keysyms.h>
0050 
0051 // specify externals before namespace
0052 
0053 namespace KWin
0054 {
0055 
0056 namespace TabBox
0057 {
0058 
0059 TabBoxHandlerImpl::TabBoxHandlerImpl(TabBox *tabBox)
0060     : TabBoxHandler(tabBox)
0061     , m_tabBox(tabBox)
0062 {
0063 }
0064 
0065 TabBoxHandlerImpl::~TabBoxHandlerImpl()
0066 {
0067 }
0068 
0069 int TabBoxHandlerImpl::activeScreen() const
0070 {
0071     return workspace()->outputs().indexOf(workspace()->activeOutput());
0072 }
0073 
0074 QString TabBoxHandlerImpl::desktopName(Window *client) const
0075 {
0076     if (!client->isOnAllDesktops()) {
0077         return client->desktops().last()->name();
0078     }
0079     return VirtualDesktopManager::self()->currentDesktop()->name();
0080 }
0081 
0082 Window *TabBoxHandlerImpl::nextClientFocusChain(Window *client) const
0083 {
0084     return Workspace::self()->focusChain()->nextMostRecentlyUsed(client);
0085 }
0086 
0087 Window *TabBoxHandlerImpl::firstClientFocusChain() const
0088 {
0089     return Workspace::self()->focusChain()->firstMostRecentlyUsed();
0090 }
0091 
0092 bool TabBoxHandlerImpl::isInFocusChain(Window *client) const
0093 {
0094     return Workspace::self()->focusChain()->contains(client);
0095 }
0096 
0097 Window *TabBoxHandlerImpl::activeClient() const
0098 {
0099     return Workspace::self()->activeWindow();
0100 }
0101 
0102 bool TabBoxHandlerImpl::checkDesktop(Window *client) const
0103 {
0104     switch (config().clientDesktopMode()) {
0105     case TabBoxConfig::AllDesktopsClients:
0106         return true;
0107     case TabBoxConfig::ExcludeCurrentDesktopClients:
0108         return !client->isOnCurrentDesktop();
0109     default: // TabBoxConfig::OnlyCurrentDesktopClients
0110         return client->isOnCurrentDesktop();
0111     }
0112 }
0113 
0114 bool TabBoxHandlerImpl::checkActivity(Window *client) const
0115 {
0116     switch (config().clientActivitiesMode()) {
0117     case TabBoxConfig::AllActivitiesClients:
0118         return true;
0119     case TabBoxConfig::ExcludeCurrentActivityClients:
0120         return !client->isOnCurrentActivity();
0121     default: // TabBoxConfig::OnlyCurrentActivityClients
0122         return client->isOnCurrentActivity();
0123     }
0124 }
0125 
0126 bool TabBoxHandlerImpl::checkApplications(Window *client) const
0127 {
0128     const auto list = clientList();
0129 
0130     switch (config().clientApplicationsMode()) {
0131     case TabBoxConfig::OneWindowPerApplication:
0132         // check if the list already contains an entry of this application
0133         for (const Window *other : list) {
0134             if (Window::belongToSameApplication(other, client, Window::SameApplicationCheck::AllowCrossProcesses)) {
0135                 return false;
0136             }
0137         }
0138         return true;
0139     case TabBoxConfig::AllWindowsCurrentApplication: {
0140         const Window *active = tabBox->activeClient();
0141         return active && Window::belongToSameApplication(active, client, Window::SameApplicationCheck::AllowCrossProcesses);
0142     }
0143     default: // TabBoxConfig::AllWindowsAllApplications
0144         return true;
0145     }
0146 }
0147 
0148 bool TabBoxHandlerImpl::checkMinimized(Window *client) const
0149 {
0150     switch (config().clientMinimizedMode()) {
0151     case TabBoxConfig::ExcludeMinimizedClients:
0152         return !client->isMinimized();
0153     case TabBoxConfig::OnlyMinimizedClients:
0154         return client->isMinimized();
0155     default: // TabBoxConfig::IgnoreMinimizedStatus
0156         return true;
0157     }
0158 }
0159 
0160 bool TabBoxHandlerImpl::checkMultiScreen(Window *client) const
0161 {
0162     switch (config().clientMultiScreenMode()) {
0163     case TabBoxConfig::IgnoreMultiScreen:
0164         return true;
0165     case TabBoxConfig::ExcludeCurrentScreenClients:
0166         return client->output() != workspace()->activeOutput();
0167     default: // TabBoxConfig::OnlyCurrentScreenClients
0168         return client->output() == workspace()->activeOutput();
0169     }
0170 }
0171 
0172 Window *TabBoxHandlerImpl::clientToAddToList(Window *client) const
0173 {
0174     if (!client || client->isDeleted()) {
0175         return nullptr;
0176     }
0177     Window *ret = nullptr;
0178 
0179     bool addClient = checkDesktop(client)
0180         && checkActivity(client)
0181         && checkApplications(client)
0182         && checkMinimized(client)
0183         && checkMultiScreen(client);
0184     addClient = addClient && client->wantsTabFocus() && !client->skipSwitcher();
0185     if (addClient) {
0186         // don't add windows that have modal dialogs
0187         Window *modal = client->findModal();
0188         if (modal == nullptr || modal == client) {
0189             ret = client;
0190         } else {
0191             if (clientList().contains(modal)) {
0192                 ret = modal;
0193             } else {
0194                 // nothing
0195             }
0196         }
0197     }
0198     return ret;
0199 }
0200 
0201 QList<Window *> TabBoxHandlerImpl::stackingOrder() const
0202 {
0203     const QList<Window *> stacking = Workspace::self()->stackingOrder();
0204     QList<Window *> ret;
0205     for (Window *window : stacking) {
0206         if (window->isClient()) {
0207             ret.append(window);
0208         }
0209     }
0210     return ret;
0211 }
0212 
0213 bool TabBoxHandlerImpl::isKWinCompositing() const
0214 {
0215     return Compositor::compositing();
0216 }
0217 
0218 void TabBoxHandlerImpl::raiseClient(Window *c) const
0219 {
0220     Workspace::self()->raiseWindow(c);
0221 }
0222 
0223 void TabBoxHandlerImpl::restack(Window *c, Window *under)
0224 {
0225     Workspace::self()->restack(c, under, true);
0226 }
0227 
0228 void TabBoxHandlerImpl::elevateClient(Window *c, QWindow *tabbox, bool b) const
0229 {
0230     c->elevate(b);
0231     if (Window *w = Workspace::self()->findInternal(tabbox)) {
0232         w->elevate(b);
0233     }
0234 }
0235 
0236 void TabBoxHandlerImpl::shadeClient(Window *c, bool b) const
0237 {
0238     c->cancelShadeHoverTimer(); // stop core shading action
0239     if (!b && c->shadeMode() == ShadeNormal) {
0240         c->setShade(ShadeHover);
0241     } else if (b && c->shadeMode() == ShadeHover) {
0242         c->setShade(ShadeNormal);
0243     }
0244 }
0245 
0246 Window *TabBoxHandlerImpl::desktopClient() const
0247 {
0248     const auto stackingOrder = Workspace::self()->stackingOrder();
0249     for (Window *window : stackingOrder) {
0250         if (window->isClient() && window->isDesktop() && window->isOnCurrentDesktop() && window->output() == workspace()->activeOutput()) {
0251             return window;
0252         }
0253     }
0254     return nullptr;
0255 }
0256 
0257 void TabBoxHandlerImpl::activateAndClose()
0258 {
0259     m_tabBox->accept();
0260 }
0261 
0262 void TabBoxHandlerImpl::highlightWindows(Window *window, QWindow *controller)
0263 {
0264     if (!effects) {
0265         return;
0266     }
0267     QList<EffectWindow *> windows;
0268     if (window) {
0269         windows << window->effectWindow();
0270     }
0271     if (Window *t = workspace()->findInternal(controller)) {
0272         windows << t->effectWindow();
0273     }
0274     effects->highlightWindows(windows);
0275 }
0276 
0277 bool TabBoxHandlerImpl::noModifierGrab() const
0278 {
0279     return m_tabBox->noModifierGrab();
0280 }
0281 
0282 /*********************************************************
0283  * TabBox
0284  *********************************************************/
0285 
0286 TabBox::TabBox()
0287     : m_displayRefcount(0)
0288     , m_tabGrab(false)
0289     , m_noModifierGrab(false)
0290     , m_forcedGlobalMouseGrab(false)
0291     , m_ready(false)
0292 {
0293     m_isShown = false;
0294     m_defaultConfig = TabBoxConfig();
0295     m_defaultConfig.setClientDesktopMode(TabBoxConfig::OnlyCurrentDesktopClients);
0296     m_defaultConfig.setClientActivitiesMode(TabBoxConfig::OnlyCurrentActivityClients);
0297     m_defaultConfig.setClientApplicationsMode(TabBoxConfig::AllWindowsAllApplications);
0298     m_defaultConfig.setOrderMinimizedMode(TabBoxConfig::NoGroupByMinimized);
0299     m_defaultConfig.setClientMinimizedMode(TabBoxConfig::IgnoreMinimizedStatus);
0300     m_defaultConfig.setShowDesktopMode(TabBoxConfig::DoNotShowDesktopClient);
0301     m_defaultConfig.setClientMultiScreenMode(TabBoxConfig::IgnoreMultiScreen);
0302     m_defaultConfig.setClientSwitchingMode(TabBoxConfig::FocusChainSwitching);
0303 
0304     m_alternativeConfig = TabBoxConfig();
0305     m_alternativeConfig.setClientDesktopMode(TabBoxConfig::AllDesktopsClients);
0306     m_alternativeConfig.setClientActivitiesMode(TabBoxConfig::OnlyCurrentActivityClients);
0307     m_alternativeConfig.setClientApplicationsMode(TabBoxConfig::AllWindowsAllApplications);
0308     m_alternativeConfig.setOrderMinimizedMode(TabBoxConfig::NoGroupByMinimized);
0309     m_alternativeConfig.setClientMinimizedMode(TabBoxConfig::IgnoreMinimizedStatus);
0310     m_alternativeConfig.setShowDesktopMode(TabBoxConfig::DoNotShowDesktopClient);
0311     m_alternativeConfig.setClientMultiScreenMode(TabBoxConfig::IgnoreMultiScreen);
0312     m_alternativeConfig.setClientSwitchingMode(TabBoxConfig::FocusChainSwitching);
0313 
0314     m_defaultCurrentApplicationConfig = m_defaultConfig;
0315     m_defaultCurrentApplicationConfig.setClientApplicationsMode(TabBoxConfig::AllWindowsCurrentApplication);
0316 
0317     m_alternativeCurrentApplicationConfig = m_alternativeConfig;
0318     m_alternativeCurrentApplicationConfig.setClientApplicationsMode(TabBoxConfig::AllWindowsCurrentApplication);
0319 
0320     m_tabBox = new TabBoxHandlerImpl(this);
0321     QTimer::singleShot(0, this, &TabBox::handlerReady);
0322 
0323     m_tabBoxMode = TabBoxWindowsMode; // init variables
0324     connect(&m_delayedShowTimer, &QTimer::timeout, this, &TabBox::show);
0325     connect(Workspace::self(), &Workspace::configChanged, this, &TabBox::reconfigure);
0326 }
0327 
0328 TabBox::~TabBox() = default;
0329 
0330 void TabBox::handlerReady()
0331 {
0332     m_tabBox->setConfig(m_defaultConfig);
0333     reconfigure();
0334     m_ready = true;
0335 }
0336 
0337 template<typename Slot>
0338 void TabBox::key(const KLazyLocalizedString &actionName, Slot slot, const QKeySequence &shortcut)
0339 {
0340     QAction *a = new QAction(this);
0341     a->setProperty("componentName", QStringLiteral("kwin"));
0342     a->setObjectName(QString::fromUtf8(actionName.untranslatedText()));
0343     a->setText(actionName.toString());
0344     KGlobalAccel::self()->setGlobalShortcut(a, QList<QKeySequence>() << shortcut);
0345     connect(a, &QAction::triggered, this, slot);
0346     auto cuts = KGlobalAccel::self()->shortcut(a);
0347     globalShortcutChanged(a, cuts.isEmpty() ? QKeySequence() : cuts.first());
0348 }
0349 
0350 static constexpr const auto s_windows = kli18n("Walk Through Windows");
0351 static constexpr const auto s_windowsRev = kli18n("Walk Through Windows (Reverse)");
0352 static constexpr const auto s_windowsAlt = kli18n("Walk Through Windows Alternative");
0353 static constexpr const auto s_windowsAltRev = kli18n("Walk Through Windows Alternative (Reverse)");
0354 static constexpr const auto s_app = kli18n("Walk Through Windows of Current Application");
0355 static constexpr const auto s_appRev = kli18n("Walk Through Windows of Current Application (Reverse)");
0356 static constexpr const auto s_appAlt = kli18n("Walk Through Windows of Current Application Alternative");
0357 static constexpr const auto s_appAltRev = kli18n("Walk Through Windows of Current Application Alternative (Reverse)");
0358 
0359 void TabBox::initShortcuts()
0360 {
0361     key(s_windows, &TabBox::slotWalkThroughWindows, Qt::ALT | Qt::Key_Tab);
0362     key(s_windowsRev, &TabBox::slotWalkBackThroughWindows, Qt::ALT | Qt::SHIFT | Qt::Key_Tab);
0363     key(s_app, &TabBox::slotWalkThroughCurrentAppWindows, Qt::ALT | Qt::Key_QuoteLeft);
0364     key(s_appRev, &TabBox::slotWalkBackThroughCurrentAppWindows, Qt::ALT | Qt::Key_AsciiTilde);
0365     key(s_windowsAlt, &TabBox::slotWalkThroughWindowsAlternative);
0366     key(s_windowsAltRev, &TabBox::slotWalkBackThroughWindowsAlternative);
0367     key(s_appAlt, &TabBox::slotWalkThroughCurrentAppWindowsAlternative);
0368     key(s_appAltRev, &TabBox::slotWalkBackThroughCurrentAppWindowsAlternative);
0369 
0370     connect(KGlobalAccel::self(), &KGlobalAccel::globalShortcutChanged, this, &TabBox::globalShortcutChanged);
0371 }
0372 
0373 void TabBox::globalShortcutChanged(QAction *action, const QKeySequence &seq)
0374 {
0375     if (qstrcmp(qPrintable(action->objectName()), s_windows.untranslatedText()) == 0) {
0376         m_cutWalkThroughWindows = seq;
0377     } else if (qstrcmp(qPrintable(action->objectName()), s_windowsRev.untranslatedText()) == 0) {
0378         m_cutWalkThroughWindowsReverse = seq;
0379     } else if (qstrcmp(qPrintable(action->objectName()), s_app.untranslatedText()) == 0) {
0380         m_cutWalkThroughCurrentAppWindows = seq;
0381     } else if (qstrcmp(qPrintable(action->objectName()), s_appRev.untranslatedText()) == 0) {
0382         m_cutWalkThroughCurrentAppWindowsReverse = seq;
0383     } else if (qstrcmp(qPrintable(action->objectName()), s_windowsAlt.untranslatedText()) == 0) {
0384         m_cutWalkThroughWindowsAlternative = seq;
0385     } else if (qstrcmp(qPrintable(action->objectName()), s_windowsAltRev.untranslatedText()) == 0) {
0386         m_cutWalkThroughWindowsAlternativeReverse = seq;
0387     } else if (qstrcmp(qPrintable(action->objectName()), s_appAlt.untranslatedText()) == 0) {
0388         m_cutWalkThroughCurrentAppWindowsAlternative = seq;
0389     } else if (qstrcmp(qPrintable(action->objectName()), s_appAltRev.untranslatedText()) == 0) {
0390         m_cutWalkThroughCurrentAppWindowsAlternativeReverse = seq;
0391     }
0392 }
0393 
0394 void TabBox::setMode(TabBoxMode mode)
0395 {
0396     m_tabBoxMode = mode;
0397     switch (mode) {
0398     case TabBoxWindowsMode:
0399         m_tabBox->setConfig(m_defaultConfig);
0400         break;
0401     case TabBoxWindowsAlternativeMode:
0402         m_tabBox->setConfig(m_alternativeConfig);
0403         break;
0404     case TabBoxCurrentAppWindowsMode:
0405         m_tabBox->setConfig(m_defaultCurrentApplicationConfig);
0406         break;
0407     case TabBoxCurrentAppWindowsAlternativeMode:
0408         m_tabBox->setConfig(m_alternativeCurrentApplicationConfig);
0409         break;
0410     }
0411 }
0412 
0413 void TabBox::reset(bool partial_reset)
0414 {
0415     m_tabBox->createModel(partial_reset);
0416     if (!partial_reset) {
0417         if (Workspace::self()->activeWindow()) {
0418             setCurrentClient(Workspace::self()->activeWindow());
0419         }
0420         // it's possible that the active client is not part of the model
0421         // in that case the index is invalid
0422         if (!m_tabBox->currentIndex().isValid()) {
0423             setCurrentIndex(m_tabBox->first());
0424         }
0425     } else {
0426         if (!m_tabBox->currentIndex().isValid() || !m_tabBox->client(m_tabBox->currentIndex())) {
0427             setCurrentIndex(m_tabBox->first());
0428         }
0429     }
0430 
0431     Q_EMIT tabBoxUpdated();
0432 }
0433 
0434 void TabBox::nextPrev(bool next)
0435 {
0436     setCurrentIndex(m_tabBox->nextPrev(next), false);
0437     Q_EMIT tabBoxUpdated();
0438 }
0439 
0440 Window *TabBox::currentClient()
0441 {
0442     if (Window *client = m_tabBox->client(m_tabBox->currentIndex())) {
0443         if (!Workspace::self()->hasWindow(client)) {
0444             return nullptr;
0445         }
0446         return client;
0447     } else {
0448         return nullptr;
0449     }
0450 }
0451 
0452 QList<Window *> TabBox::currentClientList()
0453 {
0454     return m_tabBox->clientList();
0455 }
0456 
0457 void TabBox::setCurrentClient(Window *newClient)
0458 {
0459     setCurrentIndex(m_tabBox->index(newClient));
0460 }
0461 
0462 void TabBox::setCurrentIndex(QModelIndex index, bool notifyEffects)
0463 {
0464     if (!index.isValid()) {
0465         return;
0466     }
0467     m_tabBox->setCurrentIndex(index);
0468     if (notifyEffects) {
0469         Q_EMIT tabBoxUpdated();
0470     }
0471 }
0472 
0473 void TabBox::show()
0474 {
0475     Q_EMIT tabBoxAdded(m_tabBoxMode);
0476     if (isDisplayed()) {
0477         m_isShown = false;
0478         return;
0479     }
0480     workspace()->setShowingDesktop(false);
0481     reference();
0482     m_isShown = true;
0483     m_tabBox->show();
0484 }
0485 
0486 void TabBox::hide(bool abort)
0487 {
0488     m_delayedShowTimer.stop();
0489     if (m_isShown) {
0490         m_isShown = false;
0491         unreference();
0492     }
0493     Q_EMIT tabBoxClosed();
0494     if (isDisplayed()) {
0495         qCDebug(KWIN_TABBOX) << "Tab box was not properly closed by an effect";
0496     }
0497     m_tabBox->hide(abort);
0498 }
0499 
0500 void TabBox::reconfigure()
0501 {
0502     KSharedConfigPtr c = kwinApp()->config();
0503     KConfigGroup config = c->group(QStringLiteral("TabBox"));
0504 
0505     loadConfig(c->group(QStringLiteral("TabBox")), m_defaultConfig);
0506     loadConfig(c->group(QStringLiteral("TabBoxAlternative")), m_alternativeConfig);
0507 
0508     m_defaultCurrentApplicationConfig = m_defaultConfig;
0509     m_defaultCurrentApplicationConfig.setClientApplicationsMode(TabBoxConfig::AllWindowsCurrentApplication);
0510     m_alternativeCurrentApplicationConfig = m_alternativeConfig;
0511     m_alternativeCurrentApplicationConfig.setClientApplicationsMode(TabBoxConfig::AllWindowsCurrentApplication);
0512 
0513     m_tabBox->setConfig(m_defaultConfig);
0514 
0515     m_delayShowTime = config.readEntry<int>("DelayTime", 90);
0516 
0517     QList<ElectricBorder> *borders = &m_borderActivate;
0518     QString borderConfig = QStringLiteral("BorderActivate");
0519     for (int i = 0; i < 2; ++i) {
0520         for (ElectricBorder border : std::as_const(*borders)) {
0521             workspace()->screenEdges()->unreserve(border, this);
0522         }
0523         borders->clear();
0524         QStringList list = config.readEntry(borderConfig, QStringList());
0525         for (const QString &s : std::as_const(list)) {
0526             bool ok;
0527             const int i = s.toInt(&ok);
0528             if (!ok) {
0529                 continue;
0530             }
0531             borders->append(ElectricBorder(i));
0532             workspace()->screenEdges()->reserve(ElectricBorder(i), this, "toggle");
0533         }
0534         borders = &m_borderAlternativeActivate;
0535         borderConfig = QStringLiteral("BorderAlternativeActivate");
0536     }
0537 
0538     auto touchConfig = [this, config](const QString &key, QHash<ElectricBorder, QAction *> &actions, TabBoxMode mode, const QStringList &defaults = QStringList{}) {
0539         // fist erase old config
0540         for (auto it = actions.begin(); it != actions.end();) {
0541             delete it.value();
0542             it = actions.erase(it);
0543         }
0544         // now new config
0545         const QStringList list = config.readEntry(key, defaults);
0546         for (const auto &s : list) {
0547             bool ok;
0548             const int i = s.toInt(&ok);
0549             if (!ok) {
0550                 continue;
0551             }
0552             QAction *a = new QAction(this);
0553             connect(a, &QAction::triggered, this, std::bind(&TabBox::toggleMode, this, mode));
0554             workspace()->screenEdges()->reserveTouch(ElectricBorder(i), a);
0555             actions.insert(ElectricBorder(i), a);
0556         }
0557     };
0558     touchConfig(QStringLiteral("TouchBorderActivate"), m_touchActivate, TabBoxWindowsMode);
0559     touchConfig(QStringLiteral("TouchBorderAlternativeActivate"), m_touchAlternativeActivate, TabBoxWindowsAlternativeMode);
0560 }
0561 
0562 void TabBox::loadConfig(const KConfigGroup &config, TabBoxConfig &tabBoxConfig)
0563 {
0564     tabBoxConfig.setClientDesktopMode(TabBoxConfig::ClientDesktopMode(
0565         config.readEntry<int>("DesktopMode", TabBoxConfig::defaultDesktopMode())));
0566     tabBoxConfig.setClientActivitiesMode(TabBoxConfig::ClientActivitiesMode(
0567         config.readEntry<int>("ActivitiesMode", TabBoxConfig::defaultActivitiesMode())));
0568     tabBoxConfig.setClientApplicationsMode(TabBoxConfig::ClientApplicationsMode(
0569         config.readEntry<int>("ApplicationsMode", TabBoxConfig::defaultApplicationsMode())));
0570     tabBoxConfig.setOrderMinimizedMode(TabBoxConfig::OrderMinimizedMode(
0571         config.readEntry<int>("OrderMinimizedMode", TabBoxConfig::defaultOrderMinimizedMode())));
0572     tabBoxConfig.setClientMinimizedMode(TabBoxConfig::ClientMinimizedMode(
0573         config.readEntry<int>("MinimizedMode", TabBoxConfig::defaultMinimizedMode())));
0574     tabBoxConfig.setShowDesktopMode(TabBoxConfig::ShowDesktopMode(
0575         config.readEntry<int>("ShowDesktopMode", TabBoxConfig::defaultShowDesktopMode())));
0576     tabBoxConfig.setClientMultiScreenMode(TabBoxConfig::ClientMultiScreenMode(
0577         config.readEntry<int>("MultiScreenMode", TabBoxConfig::defaultMultiScreenMode())));
0578     tabBoxConfig.setClientSwitchingMode(TabBoxConfig::ClientSwitchingMode(
0579         config.readEntry<int>("SwitchingMode", TabBoxConfig::defaultSwitchingMode())));
0580 
0581     tabBoxConfig.setShowTabBox(config.readEntry<bool>("ShowTabBox",
0582                                                       TabBoxConfig::defaultShowTabBox()));
0583     tabBoxConfig.setHighlightWindows(config.readEntry<bool>("HighlightWindows",
0584                                                             TabBoxConfig::defaultHighlightWindow()));
0585 
0586     tabBoxConfig.setLayoutName(config.readEntry<QString>("LayoutName", TabBoxConfig::defaultLayoutName()));
0587 }
0588 
0589 void TabBox::delayedShow()
0590 {
0591     if (isDisplayed() || m_delayedShowTimer.isActive()) {
0592         // already called show - no need to call it twice
0593         return;
0594     }
0595 
0596     if (!m_delayShowTime) {
0597         show();
0598         return;
0599     }
0600 
0601     m_delayedShowTimer.setSingleShot(true);
0602     m_delayedShowTimer.start(m_delayShowTime);
0603 }
0604 
0605 bool TabBox::handleMouseEvent(QMouseEvent *event)
0606 {
0607     if (!m_isShown && isDisplayed()) {
0608         // tabbox has been replaced, check effects
0609         if (effects && effects->checkInputWindowEvent(event)) {
0610             return true;
0611         }
0612     }
0613     switch (event->type()) {
0614     case QEvent::MouseMove:
0615         if (!m_tabBox->containsPos(event->globalPos())) {
0616             // filter out all events which are not on the TabBox window.
0617             // We don't want windows to react on the mouse events
0618             return true;
0619         }
0620         return false;
0621     case QEvent::MouseButtonPress:
0622         if ((!m_isShown && isDisplayed()) || !m_tabBox->containsPos(event->globalPos())) {
0623             close(); // click outside closes tab
0624             return true;
0625         }
0626         // fall through
0627     case QEvent::MouseButtonRelease:
0628     default:
0629         // we do not filter it out, the intenal filter takes care
0630         return false;
0631     }
0632     return false;
0633 }
0634 
0635 bool TabBox::handleWheelEvent(QWheelEvent *event)
0636 {
0637     if (!m_isShown && isDisplayed()) {
0638         // tabbox has been replaced, check effects
0639         if (effects && effects->checkInputWindowEvent(event)) {
0640             return true;
0641         }
0642     }
0643     if (event->angleDelta().y() == 0) {
0644         return false;
0645     }
0646     const QModelIndex index = m_tabBox->nextPrev(event->angleDelta().y() > 0);
0647     if (index.isValid()) {
0648         setCurrentIndex(index);
0649     }
0650     return true;
0651 }
0652 
0653 void TabBox::grabbedKeyEvent(QKeyEvent *event)
0654 {
0655     Q_EMIT tabBoxKeyEvent(event);
0656     if (!m_isShown && isDisplayed()) {
0657         // tabbox has been replaced, check effects
0658         return;
0659     }
0660     if (m_noModifierGrab) {
0661         if (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return || event->key() == Qt::Key_Space) {
0662             accept();
0663             return;
0664         }
0665     }
0666     m_tabBox->grabbedKeyEvent(event);
0667 }
0668 
0669 struct KeySymbolsDeleter
0670 {
0671     void operator()(xcb_key_symbols_t *symbols)
0672     {
0673         xcb_key_symbols_free(symbols);
0674     }
0675 };
0676 
0677 /**
0678  * Handles alt-tab / control-tab
0679  */
0680 static bool areKeySymXsDepressed(const uint keySyms[], int nKeySyms)
0681 {
0682     Xcb::QueryKeymap keys;
0683 
0684     std::unique_ptr<xcb_key_symbols_t, KeySymbolsDeleter> symbols(xcb_key_symbols_alloc(connection()));
0685     if (!symbols || !keys) {
0686         return false;
0687     }
0688     const auto keymap = keys->keys;
0689 
0690     bool depressed = false;
0691     for (int iKeySym = 0; iKeySym < nKeySyms; iKeySym++) {
0692         uint keySymX = keySyms[iKeySym];
0693         xcb_keycode_t *keyCodes = xcb_key_symbols_get_keycode(symbols.get(), keySymX);
0694         if (!keyCodes) {
0695             continue;
0696         }
0697 
0698         int j = 0;
0699         while (keyCodes[j] != XCB_NO_SYMBOL) {
0700             const xcb_keycode_t keyCodeX = keyCodes[j++];
0701             int i = keyCodeX / 8;
0702             char mask = 1 << (keyCodeX - (i * 8));
0703 
0704             if (i < 0 || i >= 32) {
0705                 continue;
0706             }
0707 
0708             qCDebug(KWIN_TABBOX) << iKeySym << ": keySymX=0x" << QString::number(keySymX, 16)
0709                                  << " i=" << i << " mask=0x" << QString::number(mask, 16)
0710                                  << " keymap[i]=0x" << QString::number(keymap[i], 16);
0711 
0712             if (keymap[i] & mask) {
0713                 depressed = true;
0714                 break;
0715             }
0716         }
0717 
0718         free(keyCodes);
0719     }
0720 
0721     return depressed;
0722 }
0723 
0724 static bool areModKeysDepressedX11(const QKeySequence &seq)
0725 {
0726     uint rgKeySyms[10];
0727     int nKeySyms = 0;
0728     int mod = seq[seq.count() - 1] & Qt::KeyboardModifierMask;
0729 
0730     if (mod & Qt::SHIFT) {
0731         rgKeySyms[nKeySyms++] = XK_Shift_L;
0732         rgKeySyms[nKeySyms++] = XK_Shift_R;
0733     }
0734     if (mod & Qt::CTRL) {
0735         rgKeySyms[nKeySyms++] = XK_Control_L;
0736         rgKeySyms[nKeySyms++] = XK_Control_R;
0737     }
0738     if (mod & Qt::ALT) {
0739         rgKeySyms[nKeySyms++] = XK_Alt_L;
0740         rgKeySyms[nKeySyms++] = XK_Alt_R;
0741     }
0742     if (mod & Qt::META) {
0743         // It would take some code to determine whether the Win key
0744         // is associated with Super or Meta, so check for both.
0745         // See bug #140023 for details.
0746         rgKeySyms[nKeySyms++] = XK_Super_L;
0747         rgKeySyms[nKeySyms++] = XK_Super_R;
0748         rgKeySyms[nKeySyms++] = XK_Meta_L;
0749         rgKeySyms[nKeySyms++] = XK_Meta_R;
0750     }
0751 
0752     return areKeySymXsDepressed(rgKeySyms, nKeySyms);
0753 }
0754 
0755 static bool areModKeysDepressedWayland(const QKeySequence &seq)
0756 {
0757     const int mod = seq[seq.count() - 1] & Qt::KeyboardModifierMask;
0758     const Qt::KeyboardModifiers mods = input()->modifiersRelevantForGlobalShortcuts();
0759     if ((mod & Qt::SHIFT) && mods.testFlag(Qt::ShiftModifier)) {
0760         return true;
0761     }
0762     if ((mod & Qt::CTRL) && mods.testFlag(Qt::ControlModifier)) {
0763         return true;
0764     }
0765     if ((mod & Qt::ALT) && mods.testFlag(Qt::AltModifier)) {
0766         return true;
0767     }
0768     if ((mod & Qt::META) && mods.testFlag(Qt::MetaModifier)) {
0769         return true;
0770     }
0771     return false;
0772 }
0773 
0774 static bool areModKeysDepressed(const QKeySequence &seq)
0775 {
0776     if (seq.isEmpty()) {
0777         return false;
0778     }
0779     if (kwinApp()->shouldUseWaylandForCompositing()) {
0780         return areModKeysDepressedWayland(seq);
0781     } else {
0782         return areModKeysDepressedX11(seq);
0783     }
0784 }
0785 
0786 void TabBox::navigatingThroughWindows(bool forward, const QKeySequence &shortcut, TabBoxMode mode)
0787 {
0788     if (!m_ready || isGrabbed()) {
0789         return;
0790     }
0791     if (!options->focusPolicyIsReasonable()) {
0792         // ungrabXKeyboard(); // need that because of accelerator raw mode
0793         //  CDE style raise / lower
0794         CDEWalkThroughWindows(forward);
0795     } else {
0796         if (areModKeysDepressed(shortcut)) {
0797             if (startKDEWalkThroughWindows(mode)) {
0798                 KDEWalkThroughWindows(forward);
0799             }
0800         } else {
0801             // if the shortcut has no modifiers, don't show the tabbox,
0802             // don't grab, but simply go to the next window
0803             KDEOneStepThroughWindows(forward, mode);
0804         }
0805     }
0806 }
0807 
0808 void TabBox::slotWalkThroughWindows()
0809 {
0810     navigatingThroughWindows(true, m_cutWalkThroughWindows, TabBoxWindowsMode);
0811 }
0812 
0813 void TabBox::slotWalkBackThroughWindows()
0814 {
0815     navigatingThroughWindows(false, m_cutWalkThroughWindowsReverse, TabBoxWindowsMode);
0816 }
0817 
0818 void TabBox::slotWalkThroughWindowsAlternative()
0819 {
0820     navigatingThroughWindows(true, m_cutWalkThroughWindowsAlternative, TabBoxWindowsAlternativeMode);
0821 }
0822 
0823 void TabBox::slotWalkBackThroughWindowsAlternative()
0824 {
0825     navigatingThroughWindows(false, m_cutWalkThroughWindowsAlternativeReverse, TabBoxWindowsAlternativeMode);
0826 }
0827 
0828 void TabBox::slotWalkThroughCurrentAppWindows()
0829 {
0830     navigatingThroughWindows(true, m_cutWalkThroughCurrentAppWindows, TabBoxCurrentAppWindowsMode);
0831 }
0832 
0833 void TabBox::slotWalkBackThroughCurrentAppWindows()
0834 {
0835     navigatingThroughWindows(false, m_cutWalkThroughCurrentAppWindowsReverse, TabBoxCurrentAppWindowsMode);
0836 }
0837 
0838 void TabBox::slotWalkThroughCurrentAppWindowsAlternative()
0839 {
0840     navigatingThroughWindows(true, m_cutWalkThroughCurrentAppWindowsAlternative, TabBoxCurrentAppWindowsAlternativeMode);
0841 }
0842 
0843 void TabBox::slotWalkBackThroughCurrentAppWindowsAlternative()
0844 {
0845     navigatingThroughWindows(false, m_cutWalkThroughCurrentAppWindowsAlternativeReverse, TabBoxCurrentAppWindowsAlternativeMode);
0846 }
0847 
0848 void TabBox::shadeActivate(Window *c)
0849 {
0850     if ((c->shadeMode() == ShadeNormal || c->shadeMode() == ShadeHover) && options->isShadeHover()) {
0851         c->setShade(ShadeActivated);
0852     }
0853 }
0854 
0855 bool TabBox::toggle(ElectricBorder eb)
0856 {
0857     if (m_borderAlternativeActivate.contains(eb)) {
0858         return toggleMode(TabBoxWindowsAlternativeMode);
0859     } else {
0860         return toggleMode(TabBoxWindowsMode);
0861     }
0862 }
0863 
0864 bool TabBox::toggleMode(TabBoxMode mode)
0865 {
0866     if (!options->focusPolicyIsReasonable()) {
0867         return false; // not supported.
0868     }
0869     if (isDisplayed()) {
0870         accept();
0871         return true;
0872     }
0873     if (!establishTabBoxGrab()) {
0874         return false;
0875     }
0876     m_noModifierGrab = m_tabGrab = true;
0877     setMode(mode);
0878     reset();
0879     show();
0880     return true;
0881 }
0882 
0883 bool TabBox::startKDEWalkThroughWindows(TabBoxMode mode)
0884 {
0885     if (!establishTabBoxGrab()) {
0886         return false;
0887     }
0888     m_tabGrab = true;
0889     m_noModifierGrab = false;
0890     setMode(mode);
0891     reset();
0892     return true;
0893 }
0894 
0895 void TabBox::KDEWalkThroughWindows(bool forward)
0896 {
0897     nextPrev(forward);
0898     delayedShow();
0899 }
0900 
0901 void TabBox::CDEWalkThroughWindows(bool forward)
0902 {
0903     Window *c = nullptr;
0904     // this function find the first suitable client for unreasonable focus
0905     // policies - the topmost one, with some exceptions (can't be keepabove/below,
0906     // otherwise it gets stuck on them)
0907     //     Q_ASSERT(Workspace::self()->block_stacking_updates == 0);
0908     for (int i = Workspace::self()->stackingOrder().size() - 1; i >= 0; --i) {
0909         auto t = Workspace::self()->stackingOrder().at(i);
0910         if (t->isClient() && t->isOnCurrentActivity() && t->isOnCurrentDesktop() && !t->isSpecialWindow()
0911             && !t->isShade() && t->isShown() && t->wantsTabFocus()
0912             && !t->keepAbove() && !t->keepBelow()) {
0913             c = t;
0914             break;
0915         }
0916     }
0917     Window *nc = c;
0918     bool options_traverse_all;
0919     {
0920         KConfigGroup group(kwinApp()->config(), QStringLiteral("TabBox"));
0921         options_traverse_all = group.readEntry("TraverseAll", false);
0922     }
0923 
0924     Window *firstClient = nullptr;
0925     do {
0926         nc = forward ? nextClientStatic(nc) : previousClientStatic(nc);
0927         if (!firstClient) {
0928             // When we see our first client for the second time,
0929             // it's time to stop.
0930             firstClient = nc;
0931         } else if (nc == firstClient) {
0932             // No candidates found.
0933             nc = nullptr;
0934             break;
0935         }
0936     } while (nc && nc != c && ((!options_traverse_all && !nc->isOnCurrentDesktop()) || nc->isMinimized() || !nc->wantsTabFocus() || nc->keepAbove() || nc->keepBelow() || !nc->isOnCurrentActivity()));
0937     if (nc) {
0938         if (c && c != nc) {
0939             Workspace::self()->lowerWindow(c);
0940         }
0941         if (options->focusPolicyIsReasonable()) {
0942             Workspace::self()->activateWindow(nc);
0943             shadeActivate(nc);
0944         } else {
0945             if (!nc->isOnCurrentDesktop()) {
0946                 VirtualDesktopManager::self()->setCurrent(nc->desktops().constLast());
0947             }
0948             Workspace::self()->raiseWindow(nc);
0949         }
0950     }
0951 }
0952 
0953 void TabBox::KDEOneStepThroughWindows(bool forward, TabBoxMode mode)
0954 {
0955     setMode(mode);
0956     reset();
0957     nextPrev(forward);
0958     if (Window *c = currentClient()) {
0959         Workspace::self()->activateWindow(c);
0960         shadeActivate(c);
0961     }
0962 }
0963 
0964 void TabBox::keyPress(int keyQt)
0965 {
0966     enum Direction {
0967         Backward = -1,
0968         Steady = 0,
0969         Forward = 1,
0970     };
0971     Direction direction(Steady);
0972 
0973     auto contains = [](const QKeySequence &shortcut, int key) -> bool {
0974         for (int i = 0; i < shortcut.count(); ++i) {
0975             if (shortcut[i] == key) {
0976                 return true;
0977             }
0978         }
0979         return false;
0980     };
0981 
0982     // tests whether a shortcut matches and handles pitfalls on ShiftKey invocation
0983     auto directionFor = [keyQt, contains](const QKeySequence &forward, const QKeySequence &backward) -> Direction {
0984         if (contains(forward, keyQt)) {
0985             return Forward;
0986         }
0987         if (contains(backward, keyQt)) {
0988             return Backward;
0989         }
0990         if (!(keyQt & Qt::ShiftModifier)) {
0991             return Steady;
0992         }
0993 
0994         // Before testing the unshifted key (Ctrl+A vs. Ctrl+Shift+a etc.),
0995         // see whether this is +Shift+Tab/Backtab and test that against
0996         // +Shift+Backtab/Tab as well
0997         Qt::KeyboardModifiers mods = Qt::ShiftModifier | Qt::ControlModifier | Qt::AltModifier | Qt::MetaModifier | Qt::KeypadModifier | Qt::GroupSwitchModifier;
0998         mods &= keyQt;
0999         if (((keyQt & ~mods) == Qt::Key_Tab) || ((keyQt & ~mods) == Qt::Key_Backtab)) {
1000             if (contains(forward, mods | Qt::Key_Backtab) || contains(forward, mods | Qt::Key_Tab)) {
1001                 return Forward;
1002             }
1003             if (contains(backward, mods | Qt::Key_Backtab) || contains(backward, mods | Qt::Key_Tab)) {
1004                 return Backward;
1005             }
1006         }
1007 
1008         // if the shortcuts do not match, try matching again after filtering the shift key from keyQt
1009         // it is needed to handle correctly the ALT+~ shorcut for example as it is coded as ALT+SHIFT+~ in keyQt
1010         if (contains(forward, keyQt & ~Qt::ShiftModifier)) {
1011             return Forward;
1012         }
1013         if (contains(backward, keyQt & ~Qt::ShiftModifier)) {
1014             return Backward;
1015         }
1016 
1017         return Steady;
1018     };
1019 
1020     if (m_tabGrab) {
1021         static const int ModeCount = 4;
1022         static const TabBoxMode modes[ModeCount] = {
1023             TabBoxWindowsMode, TabBoxWindowsAlternativeMode,
1024             TabBoxCurrentAppWindowsMode, TabBoxCurrentAppWindowsAlternativeMode};
1025         const QKeySequence cuts[2 * ModeCount] = {
1026             // forward
1027             m_cutWalkThroughWindows, m_cutWalkThroughWindowsAlternative,
1028             m_cutWalkThroughCurrentAppWindows, m_cutWalkThroughCurrentAppWindowsAlternative,
1029             // backward
1030             m_cutWalkThroughWindowsReverse, m_cutWalkThroughWindowsAlternativeReverse,
1031             m_cutWalkThroughCurrentAppWindowsReverse, m_cutWalkThroughCurrentAppWindowsAlternativeReverse};
1032         bool testedCurrent = false; // in case of collision, prefer to stay in the current mode
1033         int i = 0, j = 0;
1034         while (true) {
1035             if (!testedCurrent && modes[i] != mode()) {
1036                 ++j;
1037                 i = (i + 1) % ModeCount;
1038                 continue;
1039             }
1040             if (testedCurrent && modes[i] == mode()) {
1041                 break;
1042             }
1043             testedCurrent = true;
1044             direction = directionFor(cuts[i], cuts[i + ModeCount]);
1045             if (direction != Steady) {
1046                 if (modes[i] != mode()) {
1047                     accept(false);
1048                     setMode(modes[i]);
1049                     auto replayWithChangedTabboxMode = [this, direction]() {
1050                         reset();
1051                         nextPrev(direction == Forward);
1052                     };
1053                     QTimer::singleShot(50, this, replayWithChangedTabboxMode);
1054                 }
1055                 break;
1056             } else if (++j > 2 * ModeCount) { // guarding counter for invalid modes
1057                 qCDebug(KWIN_TABBOX) << "Invalid TabBoxMode";
1058                 return;
1059             }
1060             i = (i + 1) % ModeCount;
1061         }
1062         if (direction != Steady) {
1063             qCDebug(KWIN_TABBOX) << "== " << cuts[i].toString() << " or " << cuts[i + ModeCount].toString();
1064             KDEWalkThroughWindows(direction == Forward);
1065         }
1066     }
1067 
1068     if (m_tabGrab) {
1069         if (((keyQt & ~Qt::KeyboardModifierMask) == Qt::Key_Escape) && direction == Steady) {
1070             // if Escape is part of the shortcut, don't cancel
1071             close(true);
1072         } else if (direction == Steady) {
1073             QKeyEvent event(QEvent::KeyPress, keyQt & ~Qt::KeyboardModifierMask, Qt::NoModifier);
1074             grabbedKeyEvent(&event);
1075         }
1076     }
1077 }
1078 
1079 void TabBox::close(bool abort)
1080 {
1081     if (isGrabbed()) {
1082         removeTabBoxGrab();
1083     }
1084     hide(abort);
1085     input()->pointer()->setEnableConstraints(true);
1086     m_tabGrab = false;
1087     m_noModifierGrab = false;
1088 }
1089 
1090 void TabBox::accept(bool closeTabBox)
1091 {
1092     Window *c = currentClient();
1093     if (closeTabBox) {
1094         close();
1095     }
1096     if (c) {
1097         Workspace::self()->activateWindow(c);
1098         shadeActivate(c);
1099         if (c->isDesktop()) {
1100             Workspace::self()->setShowingDesktop(!Workspace::self()->showingDesktop(), !m_defaultConfig.isHighlightWindows());
1101         }
1102     }
1103 }
1104 
1105 void TabBox::modifiersReleased()
1106 {
1107     if (m_noModifierGrab) {
1108         return;
1109     }
1110     if (m_tabGrab) {
1111         accept();
1112     }
1113 }
1114 
1115 /**
1116  * Auxiliary functions to travers all clients according to the static
1117  * order. Useful for the CDE-style Alt-tab feature.
1118  */
1119 Window *TabBox::nextClientStatic(Window *c) const
1120 {
1121     const auto &list = Workspace::self()->windows();
1122     if (!c || list.isEmpty()) {
1123         return nullptr;
1124     }
1125     const int reference = list.indexOf(c);
1126     if (reference == -1) {
1127         return list.first();
1128     }
1129     for (int i = reference + 1; i < list.count(); ++i) {
1130         Window *candidate = list[i];
1131         if (candidate->isClient()) {
1132             return candidate;
1133         }
1134     }
1135     // wrap around
1136     for (int i = 0; i < reference; ++i) {
1137         Window *candidate = list[i];
1138         if (candidate->isClient()) {
1139             return candidate;
1140         }
1141     }
1142     return nullptr;
1143 }
1144 
1145 /**
1146  * Auxiliary functions to travers all clients according to the static
1147  * order. Useful for the CDE-style Alt-tab feature.
1148  */
1149 Window *TabBox::previousClientStatic(Window *c) const
1150 {
1151     const auto &list = Workspace::self()->windows();
1152     if (!c || list.isEmpty()) {
1153         return nullptr;
1154     }
1155     const int reference = list.indexOf(c);
1156     if (reference == -1) {
1157         return list.last();
1158     }
1159     for (int i = reference - 1; i >= 0; --i) {
1160         Window *candidate = list[i];
1161         if (candidate->isClient()) {
1162             return candidate;
1163         }
1164     }
1165     // wrap around
1166     for (int i = list.size() - 1; i > reference; --i) {
1167         Window *candidate = list[i];
1168         if (candidate->isClient()) {
1169             return candidate;
1170         }
1171     }
1172     return nullptr;
1173 }
1174 
1175 bool TabBox::establishTabBoxGrab()
1176 {
1177     if (kwinApp()->shouldUseWaylandForCompositing()) {
1178         m_forcedGlobalMouseGrab = true;
1179         return true;
1180     }
1181     kwinApp()->updateXTime();
1182     if (!grabXKeyboard()) {
1183         return false;
1184     }
1185     // Don't try to establish a global mouse grab using XGrabPointer, as that would prevent
1186     // using Alt+Tab while DND (#44972). However force passive grabs on all windows
1187     // in order to catch MouseRelease events and close the tabbox (#67416).
1188     // All clients already have passive grabs in their wrapper windows, so check only
1189     // the active client, which may not have it.
1190     Q_ASSERT(!m_forcedGlobalMouseGrab);
1191     m_forcedGlobalMouseGrab = true;
1192     if (Workspace::self()->activeWindow() != nullptr) {
1193         Workspace::self()->activeWindow()->updateMouseGrab();
1194     }
1195     m_x11EventFilter = std::make_unique<X11Filter>();
1196     return true;
1197 }
1198 
1199 void TabBox::removeTabBoxGrab()
1200 {
1201     if (kwinApp()->shouldUseWaylandForCompositing()) {
1202         m_forcedGlobalMouseGrab = false;
1203         return;
1204     }
1205     kwinApp()->updateXTime();
1206     ungrabXKeyboard();
1207     Q_ASSERT(m_forcedGlobalMouseGrab);
1208     m_forcedGlobalMouseGrab = false;
1209     if (Workspace::self()->activeWindow() != nullptr) {
1210         Workspace::self()->activeWindow()->updateMouseGrab();
1211     }
1212     m_x11EventFilter.reset();
1213 }
1214 } // namespace TabBox
1215 } // namespace
1216 
1217 #include "moc_tabbox.cpp"