File indexing completed on 2024-11-10 04:57:20
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"