File indexing completed on 2024-05-19 09:25:50
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: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org> 0008 SPDX-FileCopyrightText: 2022 Natalie Clarius <natalie_clarius@yahoo.de> 0009 0010 SPDX-License-Identifier: GPL-2.0-or-later 0011 */ 0012 // own 0013 #include "workspace.h" 0014 // kwin libs 0015 #include "opengl/glplatform.h" 0016 // kwin 0017 #include "core/output.h" 0018 #if KWIN_BUILD_ACTIVITIES 0019 #include "activities.h" 0020 #endif 0021 #include "appmenu.h" 0022 #include "atoms.h" 0023 #include "core/outputbackend.h" 0024 #include "core/outputconfiguration.h" 0025 #include "cursor.h" 0026 #include "dbusinterface.h" 0027 #include "effect/effecthandler.h" 0028 #include "focuschain.h" 0029 #include "group.h" 0030 #include "input.h" 0031 #include "internalwindow.h" 0032 #include "killwindow.h" 0033 #include "moving_client_x11_filter.h" 0034 #include "netinfo.h" 0035 #include "outline.h" 0036 #include "placement.h" 0037 #include "pluginmanager.h" 0038 #include "rules.h" 0039 #include "screenedge.h" 0040 #include "scripting/scripting.h" 0041 #include "syncalarmx11filter.h" 0042 #include "tiles/tilemanager.h" 0043 #include "x11window.h" 0044 #if KWIN_BUILD_TABBOX 0045 #include "tabbox/tabbox.h" 0046 #endif 0047 #include "decorations/decorationbridge.h" 0048 #include "dpmsinputeventfilter.h" 0049 #include "lidswitchtracker.h" 0050 #include "main.h" 0051 #include "outputconfigurationstore.h" 0052 #include "placeholderinputeventfilter.h" 0053 #include "placeholderoutput.h" 0054 #include "placementtracker.h" 0055 #include "tabletmodemanager.h" 0056 #include "tiles/tilemanager.h" 0057 #include "useractions.h" 0058 #include "utils/kernel.h" 0059 #include "utils/orientationsensor.h" 0060 #include "utils/xcbutils.h" 0061 #include "virtualdesktops.h" 0062 #include "was_user_interaction_x11_filter.h" 0063 #include "wayland_server.h" 0064 // KDE 0065 #include <KConfig> 0066 #include <KConfigGroup> 0067 #include <KLocalizedString> 0068 #include <KStartupInfo> 0069 // Qt 0070 #include <QCryptographicHash> 0071 #include <QDBusConnection> 0072 #include <QDBusPendingCall> 0073 #include <QMetaProperty> 0074 // xcb 0075 #include <xcb/xinerama.h> 0076 0077 namespace KWin 0078 { 0079 0080 X11EventFilterContainer::X11EventFilterContainer(X11EventFilter *filter) 0081 : m_filter(filter) 0082 { 0083 } 0084 0085 X11EventFilter *X11EventFilterContainer::filter() const 0086 { 0087 return m_filter; 0088 } 0089 0090 Workspace *Workspace::_self = nullptr; 0091 0092 Workspace::Workspace() 0093 : QObject(nullptr) 0094 // Unsorted 0095 , m_quickTileCombineTimer(nullptr) 0096 , active_popup(nullptr) 0097 , m_activePopupWindow(nullptr) 0098 , m_initialDesktop(1) 0099 , m_activeWindow(nullptr) 0100 , m_lastActiveWindow(nullptr) 0101 , m_moveResizeWindow(nullptr) 0102 , m_delayFocusWindow(nullptr) 0103 , force_restacking(false) 0104 , showing_desktop(false) 0105 , was_user_interaction(false) 0106 , block_focus(0) 0107 , m_userActionsMenu(new UserActionsMenu(this)) 0108 , m_sessionManager(new SessionManager(this)) 0109 , m_focusChain(std::make_unique<FocusChain>()) 0110 , m_applicationMenu(std::make_unique<ApplicationMenu>()) 0111 , m_placementTracker(std::make_unique<PlacementTracker>(this)) 0112 , m_outputConfigStore(std::make_unique<OutputConfigurationStore>()) 0113 , m_lidSwitchTracker(std::make_unique<LidSwitchTracker>()) 0114 , m_orientationSensor(std::make_unique<OrientationSensor>()) 0115 { 0116 _self = this; 0117 0118 #if KWIN_BUILD_ACTIVITIES 0119 if (kwinApp()->usesKActivities()) { 0120 m_activities = std::make_unique<Activities>(); 0121 } 0122 if (m_activities) { 0123 connect(m_activities.get(), &Activities::currentChanged, this, &Workspace::updateCurrentActivity); 0124 } 0125 #endif 0126 0127 delayFocusTimer = nullptr; 0128 0129 m_quickTileCombineTimer = new QTimer(this); 0130 m_quickTileCombineTimer->setSingleShot(true); 0131 0132 m_rulebook = std::make_unique<RuleBook>(); 0133 m_rulebook->load(); 0134 0135 m_screenEdges = std::make_unique<ScreenEdges>(); 0136 0137 // VirtualDesktopManager needs to be created prior to init shortcuts 0138 // and prior to TabBox, due to TabBox connecting to signals 0139 // actual initialization happens in init() 0140 VirtualDesktopManager::create(this); 0141 // dbus interface 0142 new VirtualDesktopManagerDBusInterface(VirtualDesktopManager::self()); 0143 0144 #if KWIN_BUILD_TABBOX 0145 // need to create the tabbox before compositing scene is setup 0146 m_tabbox = std::make_unique<TabBox::TabBox>(); 0147 #endif 0148 0149 m_decorationBridge = std::make_unique<Decoration::DecorationBridge>(); 0150 m_decorationBridge->init(); 0151 connect(this, &Workspace::configChanged, m_decorationBridge.get(), &Decoration::DecorationBridge::reconfigure); 0152 0153 new DBusInterface(this); 0154 m_outline = std::make_unique<Outline>(); 0155 0156 initShortcuts(); 0157 0158 init(); 0159 } 0160 0161 void Workspace::init() 0162 { 0163 KSharedConfigPtr config = kwinApp()->config(); 0164 m_screenEdges->setConfig(config); 0165 m_screenEdges->init(); 0166 connect(options, &Options::configChanged, m_screenEdges.get(), &ScreenEdges::reconfigure); 0167 connect(VirtualDesktopManager::self(), &VirtualDesktopManager::layoutChanged, m_screenEdges.get(), &ScreenEdges::updateLayout); 0168 connect(this, &Workspace::windowActivated, m_screenEdges.get(), &ScreenEdges::checkBlocking); 0169 0170 connect(this, &Workspace::windowRemoved, m_focusChain.get(), &FocusChain::remove); 0171 connect(this, &Workspace::windowActivated, m_focusChain.get(), &FocusChain::setActiveWindow); 0172 connect(VirtualDesktopManager::self(), &VirtualDesktopManager::currentChanged, m_focusChain.get(), [this]() { 0173 m_focusChain->setCurrentDesktop(VirtualDesktopManager::self()->currentDesktop()); 0174 }); 0175 connect(options, &Options::separateScreenFocusChanged, m_focusChain.get(), &FocusChain::setSeparateScreenFocus); 0176 m_focusChain->setSeparateScreenFocus(options->isSeparateScreenFocus()); 0177 0178 slotOutputBackendOutputsQueried(); 0179 connect(kwinApp()->outputBackend(), &OutputBackend::outputsQueried, this, &Workspace::slotOutputBackendOutputsQueried); 0180 0181 // create VirtualDesktopManager and perform dependency injection 0182 VirtualDesktopManager *vds = VirtualDesktopManager::self(); 0183 connect(vds, &VirtualDesktopManager::desktopAdded, this, &Workspace::slotDesktopAdded); 0184 connect(vds, &VirtualDesktopManager::desktopRemoved, this, &Workspace::slotDesktopRemoved); 0185 connect(vds, &VirtualDesktopManager::currentChanged, this, &Workspace::slotCurrentDesktopChanged); 0186 connect(vds, &VirtualDesktopManager::currentChanging, this, &Workspace::slotCurrentDesktopChanging); 0187 connect(vds, &VirtualDesktopManager::currentChangingCancelled, this, &Workspace::slotCurrentDesktopChangingCancelled); 0188 vds->setNavigationWrappingAround(options->isRollOverDesktops()); 0189 connect(options, &Options::rollOverDesktopsChanged, vds, &VirtualDesktopManager::setNavigationWrappingAround); 0190 vds->setConfig(config); 0191 0192 // Now we know how many desktops we'll have, thus we initialize the positioning object 0193 m_placement = std::make_unique<Placement>(); 0194 0195 // positioning object needs to be created before the virtual desktops are loaded. 0196 vds->load(); 0197 vds->updateLayout(); 0198 // makes sure any autogenerated id is saved, necessary as in case of xwayland, load will be called 2 times 0199 // load is needed to be called again when starting xwayalnd to sync to RootInfo, see BUG 385260 0200 vds->save(); 0201 0202 vds->setCurrent(m_initialDesktop); 0203 0204 reconfigureTimer.setSingleShot(true); 0205 updateToolWindowsTimer.setSingleShot(true); 0206 0207 connect(&reconfigureTimer, &QTimer::timeout, this, &Workspace::slotReconfigure); 0208 connect(&updateToolWindowsTimer, &QTimer::timeout, this, &Workspace::slotUpdateToolWindows); 0209 0210 // TODO: do we really need to reconfigure everything when fonts change? 0211 // maybe just reconfigure the decorations? Move this into libkdecoration? 0212 QDBusConnection::sessionBus().connect(QString(), 0213 QStringLiteral("/KDEPlatformTheme"), 0214 QStringLiteral("org.kde.KDEPlatformTheme"), 0215 QStringLiteral("refreshFonts"), 0216 this, SLOT(reconfigure())); 0217 0218 m_activeWindow = nullptr; 0219 0220 // We want to have some xcb connection while tearing down X11 components. We don't really 0221 // care if the xcb connection is broken or has an error. 0222 connect(kwinApp(), &Application::x11ConnectionChanged, this, &Workspace::initializeX11); 0223 connect(kwinApp(), &Application::x11ConnectionAboutToBeDestroyed, this, &Workspace::cleanupX11); 0224 initializeX11(); 0225 0226 Scripting::create(this); 0227 0228 if (auto server = waylandServer()) { 0229 connect(server, &WaylandServer::windowAdded, this, &Workspace::addWaylandWindow); 0230 connect(server, &WaylandServer::windowRemoved, this, &Workspace::removeWaylandWindow); 0231 } 0232 0233 // broadcast that Workspace is ready, but first process all events. 0234 QMetaObject::invokeMethod(this, &Workspace::workspaceInitialized, Qt::QueuedConnection); 0235 0236 // TODO: ungrabXServer() 0237 0238 connect(this, &Workspace::windowAdded, m_placementTracker.get(), &PlacementTracker::add); 0239 connect(this, &Workspace::windowRemoved, m_placementTracker.get(), &PlacementTracker::remove); 0240 m_placementTracker->init(getPlacementTrackerHash()); 0241 0242 const auto applySensorChanges = [this]() { 0243 m_orientationSensor->setEnabled(m_outputConfigStore->isAutoRotateActive(kwinApp()->outputBackend()->outputs(), kwinApp()->tabletModeManager()->effectiveTabletMode())); 0244 const auto opt = m_outputConfigStore->queryConfig(kwinApp()->outputBackend()->outputs(), m_lidSwitchTracker->isLidClosed(), m_orientationSensor->reading(), kwinApp()->tabletModeManager()->effectiveTabletMode()); 0245 if (opt) { 0246 const auto &[config, order, type] = *opt; 0247 applyOutputConfiguration(config, order); 0248 } 0249 }; 0250 connect(m_lidSwitchTracker.get(), &LidSwitchTracker::lidStateChanged, this, applySensorChanges); 0251 connect(m_orientationSensor.get(), &OrientationSensor::orientationChanged, this, applySensorChanges); 0252 connect(kwinApp()->tabletModeManager(), &TabletModeManager::tabletModeChanged, this, applySensorChanges); 0253 m_orientationSensor->setEnabled(m_outputConfigStore->isAutoRotateActive(kwinApp()->outputBackend()->outputs(), kwinApp()->tabletModeManager()->effectiveTabletMode())); 0254 } 0255 0256 QString Workspace::getPlacementTrackerHash() 0257 { 0258 QStringList hashes; 0259 for (const auto &output : std::as_const(m_outputs)) { 0260 QCryptographicHash hash(QCryptographicHash::Md5); 0261 if (output->edid().isValid()) { 0262 hash.addData(output->edid().raw()); 0263 } else { 0264 hash.addData(output->name().toLatin1()); 0265 } 0266 const auto geometry = output->geometry(); 0267 hash.addData(reinterpret_cast<const char *>(&geometry), sizeof(geometry)); 0268 hashes.push_back(QString::fromLatin1(hash.result().toHex())); 0269 } 0270 std::sort(hashes.begin(), hashes.end()); 0271 const auto hash = QCryptographicHash::hash(hashes.join(QString()).toLatin1(), QCryptographicHash::Md5); 0272 return QString::fromLatin1(hash.toHex()); 0273 } 0274 0275 void Workspace::initializeX11() 0276 { 0277 if (!kwinApp()->x11Connection()) { 0278 return; 0279 } 0280 0281 atoms->retrieveHelpers(); 0282 0283 // first initialize the extensions 0284 Xcb::Extensions::self(); 0285 0286 // Call this before XSelectInput() on the root window 0287 m_startup = std::make_unique<KStartupInfo>(KStartupInfo::DisableKWinModule | KStartupInfo::AnnounceSilenceChanges, this); 0288 0289 // Select windowmanager privileges 0290 selectWmInputEventMask(); 0291 0292 if (kwinApp()->operationMode() == Application::OperationModeX11) { 0293 m_wasUserInteractionFilter = std::make_unique<WasUserInteractionX11Filter>(); 0294 m_movingClientFilter = std::make_unique<MovingClientX11Filter>(); 0295 } 0296 if (Xcb::Extensions::self()->isSyncAvailable()) { 0297 m_syncAlarmFilter = std::make_unique<SyncAlarmX11Filter>(); 0298 } 0299 kwinApp()->updateXTime(); // Needed for proper initialization of user_time in Client ctor 0300 0301 const uint32_t nullFocusValues[] = {true}; 0302 m_nullFocus = std::make_unique<Xcb::Window>(QRect(-1, -1, 1, 1), XCB_WINDOW_CLASS_INPUT_ONLY, XCB_CW_OVERRIDE_REDIRECT, nullFocusValues); 0303 m_nullFocus->map(); 0304 0305 RootInfo *rootInfo = RootInfo::create(); 0306 const auto vds = VirtualDesktopManager::self(); 0307 vds->setRootInfo(rootInfo); 0308 rootInfo->activate(); 0309 0310 // TODO: only in X11 mode 0311 // Extra NETRootInfo instance in Client mode is needed to get the values of the properties 0312 NETRootInfo client_info(kwinApp()->x11Connection(), NET::ActiveWindow | NET::CurrentDesktop); 0313 bool sessionRestored = false; 0314 #ifndef QT_NO_SESSIONMANAGER 0315 sessionRestored = qApp->isSessionRestored(); 0316 #endif 0317 if (!sessionRestored) { 0318 m_initialDesktop = client_info.currentDesktop(); 0319 vds->setCurrent(m_initialDesktop); 0320 } 0321 0322 // TODO: better value 0323 rootInfo->setActiveWindow(XCB_WINDOW_NONE); 0324 focusToNull(); 0325 0326 if (!sessionRestored) { 0327 ++block_focus; // Because it will be set below 0328 } 0329 0330 { 0331 // Begin updates blocker block 0332 StackingUpdatesBlocker blocker(this); 0333 0334 Xcb::Tree tree(kwinApp()->x11RootWindow()); 0335 xcb_window_t *wins = xcb_query_tree_children(tree.data()); 0336 0337 QList<Xcb::WindowAttributes> windowAttributes(tree->children_len); 0338 QList<Xcb::WindowGeometry> windowGeometries(tree->children_len); 0339 0340 // Request the attributes and geometries of all toplevel windows 0341 for (int i = 0; i < tree->children_len; i++) { 0342 windowAttributes[i] = Xcb::WindowAttributes(wins[i]); 0343 windowGeometries[i] = Xcb::WindowGeometry(wins[i]); 0344 } 0345 0346 // Get the replies 0347 for (int i = 0; i < tree->children_len; i++) { 0348 Xcb::WindowAttributes attr(windowAttributes.at(i)); 0349 0350 if (attr.isNull()) { 0351 continue; 0352 } 0353 0354 if (attr->override_redirect) { 0355 if (attr->map_state == XCB_MAP_STATE_VIEWABLE && attr->_class != XCB_WINDOW_CLASS_INPUT_ONLY) { 0356 // ### This will request the attributes again 0357 createUnmanaged(wins[i]); 0358 } 0359 } else if (attr->map_state != XCB_MAP_STATE_UNMAPPED) { 0360 if (Application::wasCrash()) { 0361 fixPositionAfterCrash(wins[i], windowGeometries.at(i).data()); 0362 } 0363 0364 // ### This will request the attributes again 0365 createX11Window(wins[i], true); 0366 } 0367 } 0368 0369 // Propagate windows, will really happen at the end of the updates blocker block 0370 updateStackingOrder(true); 0371 0372 updateClientArea(); 0373 0374 // NETWM spec says we have to set it to (0,0) if we don't support it 0375 NETPoint *viewports = new NETPoint[VirtualDesktopManager::self()->count()]; 0376 rootInfo->setDesktopViewport(VirtualDesktopManager::self()->count(), *viewports); 0377 delete[] viewports; 0378 0379 NETSize desktop_geometry; 0380 desktop_geometry.width = m_geometry.width(); 0381 desktop_geometry.height = m_geometry.height(); 0382 rootInfo->setDesktopGeometry(desktop_geometry); 0383 0384 } // End updates blocker block 0385 0386 // TODO: only on X11? 0387 Window *newActiveWindow = nullptr; 0388 if (!sessionRestored) { 0389 --block_focus; 0390 newActiveWindow = findClient(Predicate::WindowMatch, client_info.activeWindow()); 0391 } 0392 if (newActiveWindow == nullptr && activeWindow() == nullptr && should_get_focus.count() == 0) { 0393 // No client activated in manage() 0394 if (newActiveWindow == nullptr) { 0395 newActiveWindow = topWindowOnDesktop(VirtualDesktopManager::self()->currentDesktop()); 0396 } 0397 if (newActiveWindow == nullptr) { 0398 newActiveWindow = findDesktop(true, VirtualDesktopManager::self()->currentDesktop()); 0399 } 0400 } 0401 if (newActiveWindow != nullptr) { 0402 activateWindow(newActiveWindow); 0403 } 0404 } 0405 0406 void Workspace::cleanupX11() 0407 { 0408 // We expect that other components will unregister their X11 event filters after the 0409 // connection to the X server has been lost. 0410 0411 StackingUpdatesBlocker blocker(this); 0412 0413 // Use stacking_order, so that kwin --replace keeps stacking order. 0414 const auto stack = stacking_order; 0415 for (Window *window : stack) { 0416 if (auto x11 = qobject_cast<X11Window *>(window); x11 && !x11->isDeleted()) { 0417 x11->releaseWindow(true); 0418 removeFromStack(window); 0419 } 0420 } 0421 0422 manual_overlays.clear(); 0423 0424 VirtualDesktopManager *desktopManager = VirtualDesktopManager::self(); 0425 desktopManager->setRootInfo(nullptr); 0426 0427 X11Window::cleanupX11(); 0428 RootInfo::destroy(); 0429 Xcb::Extensions::destroy(); 0430 0431 m_movingClientFilter.reset(); 0432 m_startup.reset(); 0433 m_nullFocus.reset(); 0434 m_syncAlarmFilter.reset(); 0435 m_wasUserInteractionFilter.reset(); 0436 } 0437 0438 Workspace::~Workspace() 0439 { 0440 blockStackingUpdates(true); 0441 0442 cleanupX11(); 0443 0444 if (waylandServer()) { 0445 const QList<Window *> waylandWindows = waylandServer()->windows(); 0446 for (Window *window : waylandWindows) { 0447 window->destroyWindow(); 0448 } 0449 } 0450 0451 // We need a shadow copy because windows get removed as we go through them. 0452 const QList<Window *> windows = m_windows; 0453 for (Window *window : windows) { 0454 window->destroyWindow(); 0455 } 0456 0457 m_rulebook.reset(); 0458 kwinApp()->config()->sync(); 0459 0460 m_placement.reset(); 0461 delete m_windowKeysDialog; 0462 0463 if (m_placeholderOutput) { 0464 m_placeholderOutput->unref(); 0465 } 0466 m_tileManagers.clear(); 0467 0468 for (Output *output : std::as_const(m_outputs)) { 0469 output->unref(); 0470 } 0471 0472 _self = nullptr; 0473 } 0474 0475 bool Workspace::applyOutputConfiguration(const OutputConfiguration &config, const QList<Output *> &outputOrder) 0476 { 0477 if (!kwinApp()->outputBackend()->applyOutputChanges(config)) { 0478 return false; 0479 } 0480 updateOutputs(outputOrder); 0481 m_outputConfigStore->storeConfig(kwinApp()->outputBackend()->outputs(), m_lidSwitchTracker->isLidClosed(), config, outputOrder); 0482 KConfig cfg(QStringLiteral("kdeglobals")); 0483 KConfigGroup kscreenGroup = cfg.group(QStringLiteral("KScreen")); 0484 const bool xwaylandClientsScale = kscreenGroup.readEntry("XwaylandClientsScale", true); 0485 if (xwaylandClientsScale && !outputOrder.isEmpty()) { 0486 double maxScale = 0; 0487 for (Output *output : outputOrder) { 0488 const auto changeset = config.constChangeSet(output); 0489 maxScale = std::max(maxScale, changeset ? changeset->scale.value_or(output->scale()) : output->scale()); 0490 } 0491 kwinApp()->setXwaylandScale(maxScale); 0492 } else { 0493 kwinApp()->setXwaylandScale(1); 0494 } 0495 m_orientationSensor->setEnabled(m_outputConfigStore->isAutoRotateActive(kwinApp()->outputBackend()->outputs(), kwinApp()->tabletModeManager()->effectiveTabletMode())); 0496 return true; 0497 } 0498 0499 void Workspace::updateOutputConfiguration() 0500 { 0501 // There's conflict between this code and setVirtualOutputs(), need to adjust the tests. 0502 if (QStandardPaths::isTestModeEnabled()) { 0503 return; 0504 } 0505 0506 const auto outputs = kwinApp()->outputBackend()->outputs(); 0507 if (outputs.empty()) { 0508 // nothing to do 0509 setOutputOrder({}); 0510 return; 0511 } 0512 0513 // Update the output order to a fallback list, to avoid dangling pointers 0514 const auto setFallbackOutputOrder = [this, &outputs]() { 0515 auto newOrder = outputs; 0516 newOrder.erase(std::remove_if(newOrder.begin(), newOrder.end(), [](Output *o) { 0517 return !o->isEnabled(); 0518 }), 0519 newOrder.end()); 0520 std::sort(newOrder.begin(), newOrder.end(), [](Output *left, Output *right) { 0521 return left->name() < right->name(); 0522 }); 0523 setOutputOrder(newOrder); 0524 }; 0525 0526 const auto opt = m_outputConfigStore->queryConfig(outputs, m_lidSwitchTracker->isLidClosed(), m_orientationSensor->reading(), kwinApp()->tabletModeManager()->effectiveTabletMode()); 0527 if (!opt) { 0528 return; 0529 } 0530 const auto &[cfg, order, type] = *opt; 0531 if (!applyOutputConfiguration(cfg, order)) { 0532 qCWarning(KWIN_CORE) << "Applying output config failed!"; 0533 setFallbackOutputOrder(); 0534 return; 0535 } 0536 setOutputOrder(order); 0537 if (type == OutputConfigurationStore::ConfigType::Generated) { 0538 const bool hasInternal = std::any_of(outputs.begin(), outputs.end(), [](Output *o) { 0539 return o->isInternal(); 0540 }); 0541 if (hasInternal && outputs.size() == 2) { 0542 // show the OSD with output configuration presets 0543 QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kscreen.osdService"), 0544 QStringLiteral("/org/kde/kscreen/osdService"), 0545 QStringLiteral("org.kde.kscreen.osdService"), 0546 QStringLiteral("showActionSelector")); 0547 QDBusConnection::sessionBus().asyncCall(message); 0548 } 0549 } 0550 } 0551 0552 void Workspace::setupWindowConnections(Window *window) 0553 { 0554 connect(window, &Window::minimizedChanged, this, std::bind(&Workspace::windowMinimizedChanged, this, window)); 0555 connect(window, &Window::fullScreenChanged, m_screenEdges.get(), &ScreenEdges::checkBlocking); 0556 } 0557 0558 void Workspace::constrain(Window *below, Window *above) 0559 { 0560 if (below == above) { 0561 return; 0562 } 0563 0564 QList<Constraint *> parents; 0565 QList<Constraint *> children; 0566 for (Constraint *constraint : std::as_const(m_constraints)) { 0567 if (constraint->below == below && constraint->above == above) { 0568 return; 0569 } 0570 if (constraint->below == above) { 0571 children << constraint; 0572 } else if (constraint->above == below) { 0573 parents << constraint; 0574 } 0575 } 0576 0577 Constraint *constraint = new Constraint(); 0578 constraint->parents = parents; 0579 constraint->below = below; 0580 constraint->above = above; 0581 constraint->children = children; 0582 m_constraints << constraint; 0583 0584 for (Constraint *parent : std::as_const(parents)) { 0585 parent->children << constraint; 0586 } 0587 0588 for (Constraint *child : std::as_const(children)) { 0589 child->parents << constraint; 0590 } 0591 0592 updateStackingOrder(); 0593 } 0594 0595 void Workspace::unconstrain(Window *below, Window *above) 0596 { 0597 Constraint *constraint = nullptr; 0598 for (int i = 0; i < m_constraints.count(); ++i) { 0599 if (m_constraints[i]->below == below && m_constraints[i]->above == above) { 0600 constraint = m_constraints.takeAt(i); 0601 break; 0602 } 0603 } 0604 0605 if (!constraint) { 0606 return; 0607 } 0608 0609 const QList<Constraint *> parents = constraint->parents; 0610 for (Constraint *parent : parents) { 0611 parent->children.removeOne(constraint); 0612 } 0613 0614 const QList<Constraint *> children = constraint->children; 0615 for (Constraint *child : children) { 0616 child->parents.removeOne(constraint); 0617 } 0618 0619 delete constraint; 0620 updateStackingOrder(); 0621 } 0622 0623 void Workspace::addToStack(Window *window) 0624 { 0625 // If the stacking order of a window has been restored from the session, that 0626 // window will already be in the stack when Workspace::addX11Window() is called. 0627 if (!unconstrained_stacking_order.contains(window)) { 0628 unconstrained_stacking_order.append(window); 0629 } 0630 if (!stacking_order.contains(window)) { 0631 stacking_order.append(window); 0632 } 0633 } 0634 0635 void Workspace::removeFromStack(Window *window) 0636 { 0637 unconstrained_stacking_order.removeAll(window); 0638 stacking_order.removeAll(window); 0639 0640 for (int i = m_constraints.count() - 1; i >= 0; --i) { 0641 Constraint *constraint = m_constraints[i]; 0642 const bool isBelow = (constraint->below == window); 0643 const bool isAbove = (constraint->above == window); 0644 if (!isBelow && !isAbove) { 0645 continue; 0646 } 0647 if (isBelow) { 0648 for (Constraint *child : std::as_const(constraint->children)) { 0649 child->parents.removeOne(constraint); 0650 } 0651 } else { 0652 for (Constraint *parent : std::as_const(constraint->parents)) { 0653 parent->children.removeOne(constraint); 0654 } 0655 } 0656 delete m_constraints.takeAt(i); 0657 } 0658 } 0659 0660 X11Window *Workspace::createX11Window(xcb_window_t windowId, bool is_mapped) 0661 { 0662 StackingUpdatesBlocker blocker(this); 0663 X11Window *window = new X11Window(); 0664 setupWindowConnections(window); 0665 if (!window->manage(windowId, is_mapped)) { 0666 X11Window::deleteClient(window); 0667 return nullptr; 0668 } 0669 addX11Window(window); 0670 Q_EMIT windowAdded(window); 0671 return window; 0672 } 0673 0674 X11Window *Workspace::createUnmanaged(xcb_window_t windowId) 0675 { 0676 if (kwinApp()->x11CompositeWindow() == windowId) { 0677 return nullptr; 0678 } 0679 X11Window *window = new X11Window(); 0680 if (!window->track(windowId)) { 0681 X11Window::deleteClient(window); 0682 return nullptr; 0683 } 0684 addUnmanaged(window); 0685 return window; 0686 } 0687 0688 void Workspace::addX11Window(X11Window *window) 0689 { 0690 if (showingDesktop() && breaksShowingDesktop(window)) { 0691 setShowingDesktop(false); 0692 } 0693 0694 Group *grp = findGroup(window->window()); 0695 if (grp != nullptr) { 0696 grp->gotLeader(window); 0697 } 0698 0699 if (window->isDesktop()) { 0700 if (m_activeWindow == nullptr && should_get_focus.isEmpty() && window->isOnCurrentDesktop()) { 0701 requestFocus(window); // TODO: Make sure desktop is active after startup if there's no other window active 0702 } 0703 } else { 0704 m_focusChain->update(window, FocusChain::Update); 0705 } 0706 Q_ASSERT(!m_windows.contains(window)); 0707 m_windows.append(window); 0708 addToStack(window); 0709 if (window->hasStrut()) { 0710 updateClientArea(); // This cannot be in manage(), because the window got added only now 0711 } 0712 window->updateLayer(); 0713 if (window->isDesktop()) { 0714 raiseWindow(window); 0715 // If there's no active window, make this desktop the active one 0716 if (activeWindow() == nullptr && should_get_focus.count() == 0) { 0717 activateWindow(findDesktop(true, VirtualDesktopManager::self()->currentDesktop())); 0718 } 0719 } 0720 window->checkActiveModal(); 0721 checkTransients(window->window()); // SELI TODO: Does this really belong here? 0722 updateStackingOrder(true); // Propagate new window 0723 if (window->isUtility() || window->isMenu() || window->isToolbar()) { 0724 updateToolWindows(true); 0725 } 0726 updateTabbox(); 0727 } 0728 0729 void Workspace::addUnmanaged(X11Window *window) 0730 { 0731 Q_ASSERT(!m_windows.contains(window)); 0732 m_windows.append(window); 0733 addToStack(window); 0734 updateStackingOrder(true); 0735 Q_EMIT windowAdded(window); 0736 } 0737 0738 /** 0739 * Destroys the window \a window 0740 */ 0741 void Workspace::removeX11Window(X11Window *window) 0742 { 0743 Q_ASSERT(m_windows.contains(window)); 0744 Group *group = findGroup(window->window()); 0745 if (group != nullptr) { 0746 group->lostLeader(); 0747 } 0748 removeWindow(window); 0749 } 0750 0751 void Workspace::removeUnmanaged(X11Window *window) 0752 { 0753 Q_ASSERT(m_windows.contains(window)); 0754 m_windows.removeOne(window); 0755 removeFromStack(window); 0756 updateStackingOrder(); 0757 Q_EMIT windowRemoved(window); 0758 } 0759 0760 void Workspace::addDeleted(Window *c) 0761 { 0762 Q_ASSERT(!deleted.contains(c)); 0763 deleted.append(c); 0764 } 0765 0766 void Workspace::removeDeleted(Window *c) 0767 { 0768 Q_ASSERT(deleted.contains(c)); 0769 Q_EMIT deletedRemoved(c); 0770 deleted.removeAll(c); 0771 removeFromStack(c); 0772 } 0773 0774 void Workspace::addWaylandWindow(Window *window) 0775 { 0776 if (showingDesktop() && breaksShowingDesktop(window)) { 0777 setShowingDesktop(false); 0778 } 0779 0780 setupWindowConnections(window); 0781 window->updateLayer(); 0782 0783 if (window->isPlaceable()) { 0784 const QRectF area = clientArea(PlacementArea, window, activeOutput()); 0785 bool placementDone = false; 0786 if (window->isRequestedFullScreen()) { 0787 placementDone = true; 0788 } 0789 if (window->requestedMaximizeMode() == MaximizeMode::MaximizeFull) { 0790 placementDone = true; 0791 } 0792 if (window->rules()->checkPosition(invalidPoint, true) != invalidPoint) { 0793 placementDone = true; 0794 } 0795 if (!placementDone) { 0796 m_placement->place(window, area); 0797 } 0798 } 0799 Q_ASSERT(!m_windows.contains(window)); 0800 m_windows.append(window); 0801 addToStack(window); 0802 0803 updateStackingOrder(true); 0804 if (window->hasStrut()) { 0805 updateClientArea(); 0806 } 0807 if (window->wantsInput() && !window->isMinimized()) { 0808 // Never activate a window on its own in "Extreme" mode. 0809 if (options->focusStealingPreventionLevel() < 4) { 0810 activateWindow(window); 0811 } 0812 } 0813 updateTabbox(); 0814 Q_EMIT windowAdded(window); 0815 } 0816 0817 void Workspace::removeWaylandWindow(Window *window) 0818 { 0819 windowHidden(window); 0820 removeWindow(window); 0821 } 0822 0823 void Workspace::removeWindow(Window *window) 0824 { 0825 if (window == m_activePopupWindow) { 0826 closeActivePopup(); 0827 } 0828 if (m_userActionsMenu->isMenuWindow(window)) { 0829 m_userActionsMenu->close(); 0830 } 0831 0832 m_windows.removeAll(window); 0833 if (window == m_delayFocusWindow) { 0834 cancelDelayFocus(); 0835 } 0836 attention_chain.removeAll(window); 0837 should_get_focus.removeAll(window); 0838 if (window == m_activeWindow) { 0839 m_activeWindow = nullptr; 0840 } 0841 if (window == m_lastActiveWindow) { 0842 m_lastActiveWindow = nullptr; 0843 } 0844 if (m_windowKeysWindow == window) { 0845 setupWindowShortcutDone(false); 0846 } 0847 if (window->hasStrut()) { 0848 updateClientArea(); 0849 } 0850 0851 Q_EMIT windowRemoved(window); 0852 0853 updateStackingOrder(true); 0854 updateTabbox(); 0855 } 0856 0857 void Workspace::updateToolWindows(bool also_hide) 0858 { 0859 // TODO: What if Client's transiency/group changes? should this be called too? (I'm paranoid, am I not?) 0860 if (!options->isHideUtilityWindowsForInactive()) { 0861 for (auto it = m_windows.constBegin(); it != m_windows.constEnd(); ++it) { 0862 if (X11Window *x11Window = qobject_cast<X11Window *>(*it)) { 0863 x11Window->setHidden(false); 0864 } 0865 } 0866 return; 0867 } 0868 const Group *group = nullptr; 0869 auto window = m_activeWindow; 0870 // Go up in transiency hiearchy, if the top is found, only tool transients for the top mainwindow 0871 // will be shown; if a group transient is group, all tools in the group will be shown 0872 while (window != nullptr) { 0873 if (!window->isTransient()) { 0874 break; 0875 } 0876 if (window->groupTransient()) { 0877 group = window->group(); 0878 break; 0879 } 0880 window = window->transientFor(); 0881 } 0882 // Use stacking order only to reduce flicker, it doesn't matter if block_stacking_updates == 0, 0883 // I.e. if it's not up to date 0884 0885 // SELI TODO: But maybe it should - what if a new window has been added that's not in stacking order yet? 0886 QList<Window *> to_show, to_hide; 0887 for (auto it = stacking_order.constBegin(); it != stacking_order.constEnd(); ++it) { 0888 auto c = *it; 0889 if (!c->isClient()) { 0890 continue; 0891 } 0892 if (c->isUtility() || c->isMenu() || c->isToolbar()) { 0893 bool show = true; 0894 if (!c->isTransient()) { 0895 if (!c->group() || c->group()->members().count() == 1) { // Has its own group, keep always visible 0896 show = true; 0897 } else if (window != nullptr && c->group() == window->group()) { 0898 show = true; 0899 } else { 0900 show = false; 0901 } 0902 } else { 0903 if (group != nullptr && c->group() == group) { 0904 show = true; 0905 } else if (window != nullptr && window->hasTransient(c, true)) { 0906 show = true; 0907 } else { 0908 show = false; 0909 } 0910 } 0911 if (!show && also_hide) { 0912 const auto mainwindows = c->mainWindows(); 0913 // Don't hide utility windows which are standalone(?) or 0914 // have e.g. kicker as mainwindow 0915 if (mainwindows.isEmpty()) { 0916 show = true; 0917 } 0918 for (auto it2 = mainwindows.constBegin(); it2 != mainwindows.constEnd(); ++it2) { 0919 if ((*it2)->isSpecialWindow()) { 0920 show = true; 0921 } 0922 } 0923 if (!show) { 0924 to_hide.append(c); 0925 } 0926 } 0927 if (show) { 0928 to_show.append(c); 0929 } 0930 } 0931 } // First show new ones, then hide 0932 for (int i = to_show.size() - 1; i >= 0; --i) { // From topmost 0933 // TODO: Since this is in stacking order, the order of taskbar entries changes :( 0934 to_show.at(i)->setHidden(false); 0935 } 0936 if (also_hide) { 0937 for (auto it = to_hide.constBegin(); it != to_hide.constEnd(); ++it) { // From bottommost 0938 (*it)->setHidden(true); 0939 } 0940 updateToolWindowsTimer.stop(); 0941 } else { // setActiveWindow() is after called with NULL window, quickly followed 0942 // by setting a new window, which would result in flickering 0943 resetUpdateToolWindowsTimer(); 0944 } 0945 } 0946 0947 void Workspace::resetUpdateToolWindowsTimer() 0948 { 0949 updateToolWindowsTimer.start(200); 0950 } 0951 0952 void Workspace::slotUpdateToolWindows() 0953 { 0954 updateToolWindows(true); 0955 } 0956 0957 void Workspace::slotReloadConfig() 0958 { 0959 reconfigure(); 0960 } 0961 0962 void Workspace::reconfigure() 0963 { 0964 reconfigureTimer.start(200); 0965 } 0966 0967 /** 0968 * Reread settings 0969 */ 0970 0971 void Workspace::slotReconfigure() 0972 { 0973 qCDebug(KWIN_CORE) << "Workspace::slotReconfigure()"; 0974 reconfigureTimer.stop(); 0975 0976 bool borderlessMaximizedWindows = options->borderlessMaximizedWindows(); 0977 0978 kwinApp()->config()->reparseConfiguration(); 0979 options->updateSettings(); 0980 0981 Q_EMIT configChanged(); 0982 m_userActionsMenu->discard(); 0983 updateToolWindows(true); 0984 0985 m_rulebook->load(); 0986 for (Window *window : std::as_const(m_windows)) { 0987 if (window->supportsWindowRules()) { 0988 window->evaluateWindowRules(); 0989 m_rulebook->discardUsed(window, false); 0990 } 0991 } 0992 0993 if (borderlessMaximizedWindows != options->borderlessMaximizedWindows() && !options->borderlessMaximizedWindows()) { 0994 // in case borderless maximized windows option changed and new option 0995 // is to have borders, we need to unset the borders for all maximized windows 0996 for (auto it = m_windows.cbegin(); it != m_windows.cend(); ++it) { 0997 if ((*it)->maximizeMode() == MaximizeFull) { 0998 (*it)->checkNoBorder(); 0999 } 1000 } 1001 } 1002 } 1003 1004 void Workspace::slotCurrentDesktopChanged(VirtualDesktop *oldDesktop, VirtualDesktop *newDesktop) 1005 { 1006 closeActivePopup(); 1007 ++block_focus; 1008 StackingUpdatesBlocker blocker(this); 1009 updateWindowVisibilityOnDesktopChange(newDesktop); 1010 // Restore the focus on this desktop 1011 --block_focus; 1012 1013 activateWindowOnNewDesktop(newDesktop); 1014 Q_EMIT currentDesktopChanged(oldDesktop, m_moveResizeWindow); 1015 } 1016 1017 void Workspace::slotCurrentDesktopChanging(VirtualDesktop *currentDesktop, QPointF offset) 1018 { 1019 closeActivePopup(); 1020 Q_EMIT currentDesktopChanging(currentDesktop, offset, m_moveResizeWindow); 1021 } 1022 1023 void Workspace::slotCurrentDesktopChangingCancelled() 1024 { 1025 Q_EMIT currentDesktopChangingCancelled(); 1026 } 1027 1028 void Workspace::updateWindowVisibilityOnDesktopChange(VirtualDesktop *newDesktop) 1029 { 1030 for (auto it = stacking_order.constBegin(); it != stacking_order.constEnd(); ++it) { 1031 X11Window *c = qobject_cast<X11Window *>(*it); 1032 if (!c) { 1033 continue; 1034 } 1035 if (!c->isOnDesktop(newDesktop) && c != m_moveResizeWindow && c->isOnCurrentActivity()) { 1036 (c)->updateVisibility(); 1037 } 1038 } 1039 // Now propagate the change, after hiding, before showing 1040 if (rootInfo()) { 1041 rootInfo()->setCurrentDesktop(VirtualDesktopManager::self()->current()); 1042 } 1043 1044 if (m_moveResizeWindow && !m_moveResizeWindow->isOnDesktop(newDesktop)) { 1045 m_moveResizeWindow->setDesktops({newDesktop}); 1046 } 1047 1048 for (int i = stacking_order.size() - 1; i >= 0; --i) { 1049 X11Window *c = qobject_cast<X11Window *>(stacking_order.at(i)); 1050 if (!c) { 1051 continue; 1052 } 1053 if (c->isOnDesktop(newDesktop) && c->isOnCurrentActivity()) { 1054 c->updateVisibility(); 1055 } 1056 } 1057 if (showingDesktop()) { // Do this only after desktop change to avoid flicker 1058 setShowingDesktop(false); 1059 } 1060 } 1061 1062 void Workspace::activateWindowOnNewDesktop(VirtualDesktop *desktop) 1063 { 1064 Window *window = nullptr; 1065 if (options->focusPolicyIsReasonable()) { 1066 window = findWindowToActivateOnDesktop(desktop); 1067 } 1068 // If "unreasonable focus policy" and m_activeWindow is on_all_desktops and 1069 // under mouse (Hence == old_active_window), conserve focus. 1070 // (Thanks to Volker Schatz <V.Schatz at thphys.uni-heidelberg.de>) 1071 else if (m_activeWindow && m_activeWindow->isShown() && m_activeWindow->isOnCurrentDesktop()) { 1072 window = m_activeWindow; 1073 } 1074 1075 if (!window) { 1076 window = findDesktop(true, desktop); 1077 } 1078 1079 if (window != m_activeWindow) { 1080 setActiveWindow(nullptr); 1081 } 1082 1083 if (window) { 1084 requestFocus(window); 1085 } else { 1086 focusToNull(); 1087 } 1088 } 1089 1090 Window *Workspace::findWindowToActivateOnDesktop(VirtualDesktop *desktop) 1091 { 1092 if (m_moveResizeWindow != nullptr && m_activeWindow == m_moveResizeWindow && m_focusChain->contains(m_activeWindow, desktop) && m_activeWindow->isShown() && m_activeWindow->isOnCurrentDesktop()) { 1093 // A requestFocus call will fail, as the window is already active 1094 return m_activeWindow; 1095 } 1096 // from actiavtion.cpp 1097 if (options->isNextFocusPrefersMouse()) { 1098 auto it = stackingOrder().constEnd(); 1099 while (it != stackingOrder().constBegin()) { 1100 auto window = *(--it); 1101 if (!window->isClient()) { 1102 continue; 1103 } 1104 1105 if (!(!window->isShade() && window->isShown() && window->isOnDesktop(desktop) && window->isOnCurrentActivity() && window->isOnActiveOutput())) { 1106 continue; 1107 } 1108 1109 if (window->hitTest(Cursors::self()->mouse()->pos())) { 1110 if (!window->isDesktop()) { 1111 return window; 1112 } 1113 break; // unconditional break - we do not pass the focus to some window below an unusable one 1114 } 1115 } 1116 } 1117 return m_focusChain->getForActivation(desktop); 1118 } 1119 1120 /** 1121 * Updates the current activity when it changes 1122 * do *not* call this directly; it does not set the activity. 1123 * 1124 * Shows/Hides windows according to the stacking order 1125 */ 1126 1127 void Workspace::updateCurrentActivity(const QString &new_activity) 1128 { 1129 #if KWIN_BUILD_ACTIVITIES 1130 if (!m_activities) { 1131 return; 1132 } 1133 // closeActivePopup(); 1134 ++block_focus; 1135 // TODO: Q_ASSERT( block_stacking_updates == 0 ); // Make sure stacking_order is up to date 1136 StackingUpdatesBlocker blocker(this); 1137 1138 // Optimized Desktop switching: unmapping done from back to front 1139 // mapping done from front to back => less exposure events 1140 // Notify::raise((Notify::Event) (Notify::DesktopChange+new_desktop)); 1141 1142 for (auto it = stacking_order.constBegin(); it != stacking_order.constEnd(); ++it) { 1143 X11Window *window = qobject_cast<X11Window *>(*it); 1144 if (!window) { 1145 continue; 1146 } 1147 if (!window->isOnActivity(new_activity) && window != m_moveResizeWindow && window->isOnCurrentDesktop()) { 1148 window->updateVisibility(); 1149 } 1150 } 1151 1152 // Now propagate the change, after hiding, before showing 1153 // rootInfo->setCurrentDesktop( currentDesktop() ); 1154 1155 /* TODO someday enable dragging windows to other activities 1156 if ( m_moveResizeWindow && !m_moveResizeWindow->isOnDesktop( new_desktop )) 1157 { 1158 m_moveResizeWindow->setDesktop( new_desktop ); 1159 */ 1160 1161 for (int i = stacking_order.size() - 1; i >= 0; --i) { 1162 X11Window *window = qobject_cast<X11Window *>(stacking_order.at(i)); 1163 if (!window) { 1164 continue; 1165 } 1166 if (window->isOnActivity(new_activity)) { 1167 window->updateVisibility(); 1168 } 1169 } 1170 1171 // FIXME not sure if I should do this either 1172 if (showingDesktop()) { // Do this only after desktop change to avoid flicker 1173 setShowingDesktop(false); 1174 } 1175 1176 // Restore the focus on this desktop 1177 --block_focus; 1178 Window *window = nullptr; 1179 1180 // FIXME below here is a lot of focuschain stuff, probably all wrong now 1181 // Keep active window focused if it's on the new activity 1182 if (m_activeWindow && m_activeWindow->isShown() && m_activeWindow->isOnCurrentDesktop() && m_activeWindow->isOnCurrentActivity()) { 1183 window = m_activeWindow; 1184 } else if (options->focusPolicyIsReasonable()) { 1185 // Search in focus chain 1186 window = m_focusChain->getForActivation(VirtualDesktopManager::self()->currentDesktop()); 1187 } 1188 1189 if (!window) { 1190 window = findDesktop(true, VirtualDesktopManager::self()->currentDesktop()); 1191 } 1192 1193 if (window != m_activeWindow) { 1194 setActiveWindow(nullptr); 1195 } 1196 1197 if (window) { 1198 requestFocus(window); 1199 } else { 1200 focusToNull(); 1201 } 1202 1203 Q_EMIT currentActivityChanged(); 1204 #endif 1205 } 1206 1207 Output *Workspace::outputAt(const QPointF &pos) const 1208 { 1209 Output *bestOutput = nullptr; 1210 qreal minDistance; 1211 1212 for (Output *output : std::as_const(m_outputs)) { 1213 const QRectF geo = output->geometry(); 1214 1215 const QPointF closestPoint(std::clamp(pos.x(), geo.x(), geo.x() + geo.width() - 1), 1216 std::clamp(pos.y(), geo.y(), geo.y() + geo.height() - 1)); 1217 1218 const QPointF ray = closestPoint - pos; 1219 const qreal distance = ray.x() * ray.x() + ray.y() * ray.y(); 1220 if (!bestOutput || distance < minDistance) { 1221 minDistance = distance; 1222 bestOutput = output; 1223 } 1224 } 1225 return bestOutput; 1226 } 1227 1228 Output *Workspace::findOutput(Output *reference, Direction direction, bool wrapAround) const 1229 { 1230 QList<Output *> relevantOutputs; 1231 std::copy_if(m_outputs.begin(), m_outputs.end(), std::back_inserter(relevantOutputs), [reference, direction](Output *output) { 1232 switch (direction) { 1233 case DirectionEast: 1234 case DirectionWest: 1235 // filter for outputs on same horizontal line 1236 return output->geometry().top() <= reference->geometry().bottom() && output->geometry().bottom() >= reference->geometry().top(); 1237 case DirectionSouth: 1238 case DirectionNorth: 1239 // filter for outputs on same vertical line 1240 return output->geometry().left() <= reference->geometry().right() && output->geometry().right() >= reference->geometry().left(); 1241 default: 1242 // take all outputs 1243 return true; 1244 } 1245 }); 1246 1247 std::sort(relevantOutputs.begin(), relevantOutputs.end(), [direction](const Output *o1, const Output *o2) { 1248 switch (direction) { 1249 case DirectionEast: 1250 case DirectionWest: 1251 // order outputs from left to right 1252 return o1->geometry().center().x() < o2->geometry().center().x(); 1253 case DirectionSouth: 1254 case DirectionNorth: 1255 // order outputs from top to bottom 1256 return o1->geometry().center().y() < o2->geometry().center().y(); 1257 default: 1258 // order outputs from top to bottom, then left to right 1259 // case 1: o1 is above o2 1260 // case 2: o1 is not below o2, and o1 is left of o2 1261 return o1->geometry().y() + o1->geometry().height() <= o2->geometry().top() || (o1->geometry().top() < o2->geometry().y() + o2->geometry().height() && o1->geometry().left() < o2->geometry().left()); 1262 } 1263 }); 1264 1265 const int index = relevantOutputs.indexOf(reference); 1266 Q_ASSERT(index != -1); 1267 switch (direction) { 1268 case DirectionEast: 1269 case DirectionSouth: 1270 case DirectionNext: 1271 // go forward in the list 1272 return relevantOutputs[wrapAround ? (index + 1) % relevantOutputs.count() : std::min(index + 1, (int)relevantOutputs.count() - 1)]; 1273 case DirectionWest: 1274 case DirectionNorth: 1275 case DirectionPrev: 1276 // go backward in the list 1277 return relevantOutputs[wrapAround ? (index + relevantOutputs.count() - 1) % relevantOutputs.count() : std::max(index - 1, 0)]; 1278 default: 1279 Q_UNREACHABLE(); 1280 } 1281 } 1282 1283 void Workspace::slotOutputBackendOutputsQueried() 1284 { 1285 if (waylandServer()) { 1286 updateOutputConfiguration(); 1287 } 1288 updateOutputs(); 1289 } 1290 1291 void Workspace::updateOutputs(const QList<Output *> &outputOrder) 1292 { 1293 const auto availableOutputs = kwinApp()->outputBackend()->outputs(); 1294 const auto oldOutputs = m_outputs; 1295 1296 m_outputs.clear(); 1297 for (Output *output : availableOutputs) { 1298 if (!output->isNonDesktop() && output->isEnabled()) { 1299 m_outputs.append(output); 1300 } 1301 } 1302 1303 // The workspace requires at least one output connected. 1304 if (m_outputs.isEmpty()) { 1305 if (!m_placeholderOutput) { 1306 m_placeholderOutput = new PlaceholderOutput(QSize(1920, 1080), 1); 1307 m_placeholderFilter = std::make_unique<PlaceholderInputEventFilter>(); 1308 input()->prependInputEventFilter(m_placeholderFilter.get()); 1309 } 1310 m_outputs.append(m_placeholderOutput); 1311 } else { 1312 if (m_placeholderOutput) { 1313 m_placeholderOutput->unref(); 1314 m_placeholderOutput = nullptr; 1315 m_placeholderFilter.reset(); 1316 } 1317 } 1318 1319 if (!m_activeOutput || !m_outputs.contains(m_activeOutput)) { 1320 setActiveOutput(m_outputs[0]); 1321 } 1322 if (!m_outputs.contains(m_activeCursorOutput)) { 1323 m_activeCursorOutput = nullptr; 1324 } 1325 1326 if (!outputOrder.empty()) { 1327 setOutputOrder(outputOrder); 1328 } else { 1329 // ensure all enabled but no disabled outputs are in the output order 1330 for (Output *output : std::as_const(m_outputs)) { 1331 if (output->isEnabled() && !m_outputOrder.contains(output)) { 1332 m_outputOrder.push_back(output); 1333 } 1334 } 1335 m_outputOrder.erase(std::remove_if(m_outputOrder.begin(), m_outputOrder.end(), [this](Output *output) { 1336 return !m_outputs.contains(output); 1337 }), 1338 m_outputOrder.end()); 1339 } 1340 1341 const QSet<Output *> oldOutputsSet(oldOutputs.constBegin(), oldOutputs.constEnd()); 1342 const QSet<Output *> outputsSet(m_outputs.constBegin(), m_outputs.constEnd()); 1343 1344 const auto added = outputsSet - oldOutputsSet; 1345 for (Output *output : added) { 1346 output->ref(); 1347 m_tileManagers[output] = std::make_unique<TileManager>(output); 1348 connect(output, &Output::aboutToTurnOff, this, &Workspace::createDpmsFilter); 1349 connect(output, &Output::wakeUp, this, &Workspace::maybeDestroyDpmsFilter); 1350 if (output->dpmsMode() != Output::DpmsMode::On) { 1351 createDpmsFilter(); 1352 } 1353 Q_EMIT outputAdded(output); 1354 } 1355 maybeDestroyDpmsFilter(); 1356 1357 const auto removed = oldOutputsSet - outputsSet; 1358 for (Output *output : removed) { 1359 Q_EMIT outputRemoved(output); 1360 auto tileManager = std::move(m_tileManagers[output]); 1361 m_tileManagers.erase(output); 1362 1363 // Evacuate windows from the defunct custom tile tree. 1364 tileManager->rootTile()->visitDescendants([](const Tile *child) { 1365 const QList<Window *> windows = child->windows(); 1366 for (Window *window : windows) { 1367 window->setTile(nullptr); 1368 } 1369 }); 1370 1371 // Migrate windows from the defunct quick tile to a quick tile tree on another output. 1372 static constexpr QuickTileMode quickTileModes[] = { 1373 QuickTileFlag::Left, 1374 QuickTileFlag::Right, 1375 QuickTileFlag::Top, 1376 QuickTileFlag::Bottom, 1377 QuickTileFlag::Top | QuickTileFlag::Left, 1378 QuickTileFlag::Top | QuickTileFlag::Right, 1379 QuickTileFlag::Bottom | QuickTileFlag::Left, 1380 QuickTileFlag::Bottom | QuickTileFlag::Right, 1381 }; 1382 1383 for (const QuickTileMode &quickTileMode : quickTileModes) { 1384 Tile *quickTile = tileManager->quickTile(quickTileMode); 1385 const QList<Window *> windows = quickTile->windows(); 1386 if (windows.isEmpty()) { 1387 continue; 1388 } 1389 1390 Output *bestOutput = outputAt(output->geometry().center()); 1391 Tile *bestTile = m_tileManagers[bestOutput]->quickTile(quickTileMode); 1392 1393 for (Window *window : windows) { 1394 window->setTile(bestTile); 1395 } 1396 } 1397 } 1398 1399 desktopResized(); 1400 1401 for (Output *output : removed) { 1402 output->unref(); 1403 } 1404 1405 Q_EMIT outputsChanged(); 1406 } 1407 1408 void Workspace::createDpmsFilter() 1409 { 1410 if (!m_dpmsFilter) { 1411 m_dpmsFilter = std::make_unique<DpmsInputEventFilter>(); 1412 input()->prependInputEventFilter(m_dpmsFilter.get()); 1413 } 1414 } 1415 1416 void Workspace::maybeDestroyDpmsFilter() 1417 { 1418 const bool allOn = std::all_of(m_outputs.begin(), m_outputs.end(), [](Output *output) { 1419 return output->dpmsMode() == Output::DpmsMode::On && !output->isPlaceholder(); 1420 }); 1421 if (allOn) { 1422 m_dpmsFilter.reset(); 1423 } 1424 } 1425 1426 void Workspace::slotDesktopAdded(VirtualDesktop *desktop) 1427 { 1428 m_focusChain->addDesktop(desktop); 1429 m_placement->reinitCascading(); 1430 updateClientArea(); 1431 } 1432 1433 void Workspace::slotDesktopRemoved(VirtualDesktop *desktop) 1434 { 1435 for (auto it = m_windows.constBegin(); it != m_windows.constEnd(); ++it) { 1436 if (!(*it)->desktops().contains(desktop)) { 1437 continue; 1438 } 1439 if ((*it)->desktops().count() > 1) { 1440 (*it)->leaveDesktop(desktop); 1441 } else { 1442 const uint desktopId = std::min(desktop->x11DesktopNumber(), VirtualDesktopManager::self()->count()); 1443 sendWindowToDesktops(*it, {VirtualDesktopManager::self()->desktopForX11Id(desktopId)}, true); 1444 } 1445 } 1446 1447 for (auto it = deleted.constBegin(); it != deleted.constEnd(); ++it) { 1448 if ((*it)->desktops().contains(desktop)) { 1449 (*it)->leaveDesktop(desktop); 1450 } 1451 } 1452 1453 updateClientArea(); 1454 m_placement->reinitCascading(); 1455 m_focusChain->removeDesktop(desktop); 1456 } 1457 1458 void Workspace::selectWmInputEventMask() 1459 { 1460 uint32_t presentMask = 0; 1461 Xcb::WindowAttributes attr(kwinApp()->x11RootWindow()); 1462 if (!attr.isNull()) { 1463 presentMask = attr->your_event_mask; 1464 } 1465 1466 const uint32_t wmMask = XCB_EVENT_MASK_KEY_PRESS 1467 | XCB_EVENT_MASK_PROPERTY_CHANGE 1468 | XCB_EVENT_MASK_COLOR_MAP_CHANGE 1469 | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT 1470 | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY 1471 | XCB_EVENT_MASK_FOCUS_CHANGE // For NotifyDetailNone 1472 | XCB_EVENT_MASK_EXPOSURE; 1473 1474 Xcb::selectInput(kwinApp()->x11RootWindow(), presentMask | wmMask); 1475 } 1476 1477 /** 1478 * Sends window \a window to desktop \a desk. 1479 * 1480 * Takes care of transients as well. 1481 */ 1482 void Workspace::sendWindowToDesktops(Window *window, const QList<VirtualDesktop *> &desktops, bool dont_activate) 1483 { 1484 const QList<VirtualDesktop *> oldDesktops = window->desktops(); 1485 const bool wasOnCurrent = window->isOnCurrentDesktop(); 1486 window->setDesktops(desktops); 1487 if (window->desktops() != desktops) { // No change or desktop forced 1488 return; 1489 } 1490 1491 if (window->isOnCurrentDesktop()) { 1492 if (window->wantsTabFocus() && options->focusPolicyIsReasonable() && !wasOnCurrent && // for stickyness changes 1493 !dont_activate) { 1494 requestFocus(window); 1495 } else { 1496 restackWindowUnderActive(window); 1497 } 1498 } else { 1499 raiseWindow(window); 1500 } 1501 1502 window->checkWorkspacePosition(QRect(), oldDesktops.isEmpty() ? nullptr : oldDesktops.last()); 1503 1504 auto transients_stacking_order = ensureStackingOrder(window->transients()); 1505 for (auto it = transients_stacking_order.constBegin(); it != transients_stacking_order.constEnd(); ++it) { 1506 sendWindowToDesktops(*it, window->desktops(), dont_activate); 1507 } 1508 updateClientArea(); 1509 } 1510 1511 void Workspace::sendWindowToOutput(Window *window, Output *output) 1512 { 1513 window->sendToOutput(output); 1514 } 1515 1516 /** 1517 * Delayed focus functions 1518 */ 1519 void Workspace::delayFocus() 1520 { 1521 requestFocus(m_delayFocusWindow); 1522 cancelDelayFocus(); 1523 } 1524 1525 void Workspace::requestDelayFocus(Window *window) 1526 { 1527 m_delayFocusWindow = window; 1528 delete delayFocusTimer; 1529 delayFocusTimer = new QTimer(this); 1530 connect(delayFocusTimer, &QTimer::timeout, this, &Workspace::delayFocus); 1531 delayFocusTimer->setSingleShot(true); 1532 delayFocusTimer->start(options->delayFocusInterval()); 1533 } 1534 1535 void Workspace::cancelDelayFocus() 1536 { 1537 delete delayFocusTimer; 1538 delayFocusTimer = nullptr; 1539 m_delayFocusWindow = nullptr; 1540 } 1541 1542 bool Workspace::checkStartupNotification(xcb_window_t w, KStartupInfoId &id, KStartupInfoData &data) 1543 { 1544 return m_startup->checkStartup(w, id, data) == KStartupInfo::Match; 1545 } 1546 1547 /** 1548 * Puts the focus on a dummy window 1549 * Just using XSetInputFocus() with None would block keyboard input 1550 */ 1551 void Workspace::focusToNull() 1552 { 1553 if (m_nullFocus) { 1554 should_get_focus.clear(); 1555 m_nullFocus->focus(); 1556 } 1557 } 1558 1559 bool Workspace::breaksShowingDesktop(Window *window) const 1560 { 1561 return !(window->isUnmanaged() || window->isDock() || window->isDesktop() || window->belongsToDesktop()); 1562 } 1563 1564 void Workspace::setShowingDesktop(bool showing, bool animated) 1565 { 1566 const bool changed = showing != showing_desktop; 1567 if (rootInfo() && changed) { 1568 rootInfo()->setShowingDesktop(showing); 1569 } 1570 showing_desktop = showing; 1571 1572 for (int i = stacking_order.count() - 1; i > -1; --i) { 1573 auto window = stacking_order.at(i); 1574 if (window->isDeleted()) { 1575 continue; 1576 } 1577 if (breaksShowingDesktop(window)) { 1578 window->setHiddenByShowDesktop(showing_desktop); 1579 } 1580 } 1581 1582 if (showing_desktop) { 1583 Window *desktop = findDesktop(true, VirtualDesktopManager::self()->currentDesktop()); 1584 if (desktop) { 1585 requestFocus(desktop); 1586 } 1587 } else if (!showing_desktop && changed) { 1588 const auto window = m_focusChain->getForActivation(VirtualDesktopManager::self()->currentDesktop()); 1589 if (window) { 1590 activateWindow(window); 1591 } 1592 } 1593 if (changed) { 1594 Q_EMIT showingDesktopChanged(showing, animated); 1595 } 1596 } 1597 1598 void Workspace::disableGlobalShortcutsForClient(bool disable) 1599 { 1600 if (m_globalShortcutsDisabledForWindow == disable) { 1601 return; 1602 } 1603 QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kglobalaccel"), 1604 QStringLiteral("/kglobalaccel"), 1605 QStringLiteral("org.kde.KGlobalAccel"), 1606 QStringLiteral("blockGlobalShortcuts")); 1607 message.setArguments(QList<QVariant>() << disable); 1608 QDBusConnection::sessionBus().asyncCall(message); 1609 1610 m_globalShortcutsDisabledForWindow = disable; 1611 // Update also Meta+LMB actions etc. 1612 for (auto it = m_windows.constBegin(); it != m_windows.constEnd(); ++it) { 1613 (*it)->updateMouseGrab(); 1614 } 1615 } 1616 1617 QString Workspace::supportInformation() const 1618 { 1619 QString support; 1620 const QString yes = QStringLiteral("yes\n"); 1621 const QString no = QStringLiteral("no\n"); 1622 1623 support.append(ki18nc("Introductory text shown in the support information.", 1624 "KWin Support Information:\n" 1625 "The following information should be used when requesting support on e.g. https://discuss.kde.org.\n" 1626 "It provides information about the currently running instance, which options are used,\n" 1627 "what OpenGL driver and which effects are running.\n" 1628 "Please post the information provided underneath this introductory text to a paste bin service\n" 1629 "like https://paste.kde.org instead of pasting into support threads.\n") 1630 .toString()); 1631 support.append(QStringLiteral("\n==========================\n\n")); 1632 // all following strings are intended for support. They need to be pasted to e.g forums.kde.org 1633 // it is expected that the support will happen in English language or that the people providing 1634 // help understand English. Because of that all texts are not translated 1635 support.append(QStringLiteral("Version\n")); 1636 support.append(QStringLiteral("=======\n")); 1637 support.append(QStringLiteral("KWin version: ")); 1638 support.append(KWIN_VERSION_STRING); 1639 support.append(QStringLiteral("\n")); 1640 support.append(QStringLiteral("Qt Version: ")); 1641 support.append(QString::fromUtf8(qVersion())); 1642 support.append(QStringLiteral("\n")); 1643 support.append(QStringLiteral("Qt compile version: %1\n").arg(QStringLiteral(QT_VERSION_STR))); 1644 support.append(QStringLiteral("XCB compile version: %1\n\n").arg(XCB_VERSION_STRING)); 1645 support.append(QStringLiteral("Operation Mode: ")); 1646 switch (kwinApp()->operationMode()) { 1647 case Application::OperationModeX11: 1648 support.append(QStringLiteral("X11 only")); 1649 break; 1650 case Application::OperationModeWaylandOnly: 1651 support.append(QStringLiteral("Wayland Only")); 1652 break; 1653 case Application::OperationModeXwayland: 1654 support.append(QStringLiteral("Xwayland")); 1655 break; 1656 } 1657 support.append(QStringLiteral("\n\n")); 1658 1659 support.append(QStringLiteral("Build Options\n")); 1660 support.append(QStringLiteral("=============\n")); 1661 1662 support.append(QStringLiteral("KWIN_BUILD_DECORATIONS: ")); 1663 support.append(KWIN_BUILD_DECORATIONS ? yes : no); 1664 support.append(QStringLiteral("KWIN_BUILD_TABBOX: ")); 1665 support.append(KWIN_BUILD_TABBOX ? yes : no); 1666 support.append(QStringLiteral("KWIN_BUILD_ACTIVITIES: ")); 1667 support.append(KWIN_BUILD_ACTIVITIES ? yes : no); 1668 support.append(QStringLiteral("HAVE_X11_XCB: ")); 1669 support.append(HAVE_X11_XCB ? yes : no); 1670 support.append(QStringLiteral("HAVE_GLX: ")); 1671 support.append(HAVE_GLX ? yes : no); 1672 support.append(QStringLiteral("\n")); 1673 1674 if (auto c = kwinApp()->x11Connection()) { 1675 support.append(QStringLiteral("X11\n")); 1676 support.append(QStringLiteral("===\n")); 1677 auto x11setup = xcb_get_setup(c); 1678 support.append(QStringLiteral("Vendor: %1\n").arg(QString::fromUtf8(QByteArray::fromRawData(xcb_setup_vendor(x11setup), xcb_setup_vendor_length(x11setup))))); 1679 support.append(QStringLiteral("Vendor Release: %1\n").arg(x11setup->release_number)); 1680 support.append(QStringLiteral("Protocol Version/Revision: %1/%2\n").arg(x11setup->protocol_major_version).arg(x11setup->protocol_minor_version)); 1681 const auto extensions = Xcb::Extensions::self()->extensions(); 1682 for (const auto &e : extensions) { 1683 support.append(QStringLiteral("%1: %2; Version: 0x%3\n") 1684 .arg(QString::fromUtf8(e.name), e.present ? yes.trimmed() : no.trimmed(), QString::number(e.version, 16))); 1685 } 1686 support.append(QStringLiteral("\n")); 1687 } 1688 1689 if (m_decorationBridge) { 1690 support.append(QStringLiteral("Decoration\n")); 1691 support.append(QStringLiteral("==========\n")); 1692 support.append(m_decorationBridge->supportInformation()); 1693 support.append(QStringLiteral("\n")); 1694 } 1695 support.append(QStringLiteral("Output backend\n")); 1696 support.append(QStringLiteral("==============\n")); 1697 support.append(kwinApp()->outputBackend()->supportInformation()); 1698 support.append(QStringLiteral("\n")); 1699 1700 const Cursor *cursor = Cursors::self()->mouse(); 1701 support.append(QLatin1String("Cursor\n")); 1702 support.append(QLatin1String("======\n")); 1703 support.append(QLatin1String("themeName: ") + cursor->themeName() + QLatin1Char('\n')); 1704 support.append(QLatin1String("themeSize: ") + QString::number(cursor->themeSize()) + QLatin1Char('\n')); 1705 support.append(QLatin1Char('\n')); 1706 1707 support.append(QStringLiteral("Options\n")); 1708 support.append(QStringLiteral("=======\n")); 1709 const QMetaObject *metaOptions = options->metaObject(); 1710 auto printProperty = [](const QVariant &variant) { 1711 if (variant.type() == QVariant::Size) { 1712 const QSize &s = variant.toSize(); 1713 return QStringLiteral("%1x%2").arg(s.width()).arg(s.height()); 1714 } 1715 if (QLatin1String(variant.typeName()) == QLatin1String("KWin::OpenGLPlatformInterface") || QLatin1String(variant.typeName()) == QLatin1String("KWin::Options::WindowOperation")) { 1716 return QString::number(variant.toInt()); 1717 } 1718 return variant.toString(); 1719 }; 1720 for (int i = 0; i < metaOptions->propertyCount(); ++i) { 1721 const QMetaProperty property = metaOptions->property(i); 1722 if (QLatin1String(property.name()) == QLatin1String("objectName")) { 1723 continue; 1724 } 1725 support.append(QStringLiteral("%1: %2\n").arg(property.name(), printProperty(options->property(property.name())))); 1726 } 1727 support.append(QStringLiteral("\nScreen Edges\n")); 1728 support.append(QStringLiteral("============\n")); 1729 const QMetaObject *metaScreenEdges = m_screenEdges->metaObject(); 1730 for (int i = 0; i < metaScreenEdges->propertyCount(); ++i) { 1731 const QMetaProperty property = metaScreenEdges->property(i); 1732 if (QLatin1String(property.name()) == QLatin1String("objectName")) { 1733 continue; 1734 } 1735 support.append(QStringLiteral("%1: %2\n").arg(property.name(), printProperty(m_screenEdges->property(property.name())))); 1736 } 1737 support.append(QStringLiteral("\nScreens\n")); 1738 support.append(QStringLiteral("=======\n")); 1739 support.append(QStringLiteral("Active screen follows mouse: ")); 1740 if (options->activeMouseScreen()) { 1741 support.append(QStringLiteral(" yes\n")); 1742 } else { 1743 support.append(QStringLiteral(" no\n")); 1744 } 1745 const QList<Output *> outputs = kwinApp()->outputBackend()->outputs(); 1746 support.append(QStringLiteral("Number of Screens: %1\n\n").arg(outputs.count())); 1747 for (int i = 0; i < outputs.count(); ++i) { 1748 const auto output = outputs[i]; 1749 const QRect geo = outputs[i]->geometry(); 1750 support.append(QStringLiteral("Screen %1:\n").arg(i)); 1751 support.append(QStringLiteral("---------\n")); 1752 support.append(QStringLiteral("Name: %1\n").arg(output->name())); 1753 support.append(QStringLiteral("Enabled: %1\n").arg(output->isEnabled())); 1754 if (output->isEnabled()) { 1755 support.append(QStringLiteral("Geometry: %1,%2,%3x%4\n") 1756 .arg(geo.x()) 1757 .arg(geo.y()) 1758 .arg(geo.width()) 1759 .arg(geo.height())); 1760 support.append(QStringLiteral("Scale: %1\n").arg(output->scale())); 1761 support.append(QStringLiteral("Refresh Rate: %1\n").arg(output->refreshRate())); 1762 QString vrr = QStringLiteral("incapable"); 1763 if (output->capabilities() & Output::Capability::Vrr) { 1764 switch (output->vrrPolicy()) { 1765 case VrrPolicy::Never: 1766 vrr = QStringLiteral("never"); 1767 break; 1768 case VrrPolicy::Always: 1769 vrr = QStringLiteral("always"); 1770 break; 1771 case VrrPolicy::Automatic: 1772 vrr = QStringLiteral("automatic"); 1773 break; 1774 } 1775 } 1776 support.append(QStringLiteral("Adaptive Sync: %1\n").arg(vrr)); 1777 } 1778 } 1779 support.append(QStringLiteral("\nCompositing\n")); 1780 support.append(QStringLiteral("===========\n")); 1781 if (effects) { 1782 support.append(QStringLiteral("Compositing is active\n")); 1783 switch (effects->compositingType()) { 1784 case OpenGLCompositing: { 1785 GLPlatform *platform = GLPlatform::instance(); 1786 if (platform->isGLES()) { 1787 support.append(QStringLiteral("Compositing Type: OpenGL ES 2.0\n")); 1788 } else { 1789 support.append(QStringLiteral("Compositing Type: OpenGL\n")); 1790 } 1791 support.append(QStringLiteral("OpenGL vendor string: ") + QString::fromUtf8(platform->glVendorString()) + QStringLiteral("\n")); 1792 support.append(QStringLiteral("OpenGL renderer string: ") + QString::fromUtf8(platform->glRendererString()) + QStringLiteral("\n")); 1793 support.append(QStringLiteral("OpenGL version string: ") + QString::fromUtf8(platform->glVersionString()) + QStringLiteral("\n")); 1794 support.append(QStringLiteral("OpenGL platform interface: ")); 1795 switch (platform->platformInterface()) { 1796 case GlxPlatformInterface: 1797 support.append(QStringLiteral("GLX")); 1798 break; 1799 case EglPlatformInterface: 1800 support.append(QStringLiteral("EGL")); 1801 break; 1802 default: 1803 support.append(QStringLiteral("UNKNOWN")); 1804 } 1805 support.append(QStringLiteral("\n")); 1806 1807 support.append(QStringLiteral("OpenGL shading language version string: ") + QString::fromUtf8(platform->glShadingLanguageVersionString()) + QStringLiteral("\n")); 1808 1809 support.append(QStringLiteral("Driver: ") + GLPlatform::driverToString(platform->driver()) + QStringLiteral("\n")); 1810 if (!platform->isMesaDriver()) { 1811 support.append(QStringLiteral("Driver version: ") + platform->driverVersion().toString() + QStringLiteral("\n")); 1812 } 1813 1814 support.append(QStringLiteral("GPU class: ") + GLPlatform::chipClassToString(platform->chipClass()) + QStringLiteral("\n")); 1815 1816 support.append(QStringLiteral("OpenGL version: ") + platform->glVersion().toString() + QStringLiteral("\n")); 1817 1818 support.append(QStringLiteral("GLSL version: ") + platform->glslVersion().toString() + QStringLiteral("\n")); 1819 1820 if (platform->isMesaDriver()) { 1821 support.append(QStringLiteral("Mesa version: ") + platform->mesaVersion().toString() + QStringLiteral("\n")); 1822 } 1823 if (auto xVersion = Xcb::xServerVersion(); xVersion.isValid()) { 1824 support.append(QStringLiteral("X server version: ") + xVersion.toString() + QStringLiteral("\n")); 1825 } 1826 if (auto kernelVersion = linuxKernelVersion(); kernelVersion.isValid()) { 1827 support.append(QStringLiteral("Linux kernel version: ") + kernelVersion.toString() + QStringLiteral("\n")); 1828 } 1829 1830 support.append(QStringLiteral("Direct rendering: ")); 1831 support.append(QStringLiteral("Requires strict binding: ")); 1832 if (!platform->isLooseBinding()) { 1833 support.append(QStringLiteral("yes\n")); 1834 } else { 1835 support.append(QStringLiteral("no\n")); 1836 } 1837 support.append(QStringLiteral("Virtual Machine: ")); 1838 if (platform->isVirtualMachine()) { 1839 support.append(QStringLiteral(" yes\n")); 1840 } else { 1841 support.append(QStringLiteral(" no\n")); 1842 } 1843 1844 support.append(QStringLiteral("OpenGL 2 Shaders are used\n")); 1845 break; 1846 } 1847 case QPainterCompositing: 1848 support.append("Compositing Type: QPainter\n"); 1849 break; 1850 case NoCompositing: 1851 default: 1852 support.append(QStringLiteral("Something is really broken, neither OpenGL nor QPainter is used")); 1853 } 1854 support.append(QStringLiteral("\nLoaded Effects:\n")); 1855 support.append(QStringLiteral("---------------\n")); 1856 const auto loadedEffects = effects->loadedEffects(); 1857 for (const QString &effect : loadedEffects) { 1858 support.append(effect + QStringLiteral("\n")); 1859 } 1860 support.append(QStringLiteral("\nCurrently Active Effects:\n")); 1861 support.append(QStringLiteral("-------------------------\n")); 1862 const auto activeEffects = effects->activeEffects(); 1863 for (const QString &effect : activeEffects) { 1864 support.append(effect + QStringLiteral("\n")); 1865 } 1866 support.append(QStringLiteral("\nEffect Settings:\n")); 1867 support.append(QStringLiteral("----------------\n")); 1868 for (const QString &effect : loadedEffects) { 1869 support.append(effects->supportInformation(effect)); 1870 support.append(QStringLiteral("\n")); 1871 } 1872 support.append(QLatin1String("\nLoaded Plugins:\n")); 1873 support.append(QLatin1String("---------------\n")); 1874 QStringList loadedPlugins = kwinApp()->pluginManager()->loadedPlugins(); 1875 loadedPlugins.sort(); 1876 for (const QString &plugin : std::as_const(loadedPlugins)) { 1877 support.append(plugin + QLatin1Char('\n')); 1878 } 1879 support.append(QLatin1String("\nAvailable Plugins:\n")); 1880 support.append(QLatin1String("------------------\n")); 1881 QStringList availablePlugins = kwinApp()->pluginManager()->availablePlugins(); 1882 availablePlugins.sort(); 1883 for (const QString &plugin : std::as_const(availablePlugins)) { 1884 support.append(plugin + QLatin1Char('\n')); 1885 } 1886 } else { 1887 support.append(QStringLiteral("Compositing is not active\n")); 1888 } 1889 return support; 1890 } 1891 1892 void Workspace::forEachClient(std::function<void(X11Window *)> func) 1893 { 1894 for (Window *window : std::as_const(m_windows)) { 1895 X11Window *x11Window = qobject_cast<X11Window *>(window); 1896 if (x11Window && !x11Window->isUnmanaged()) { 1897 func(x11Window); 1898 } 1899 } 1900 } 1901 1902 X11Window *Workspace::findClient(std::function<bool(const X11Window *)> func) const 1903 { 1904 for (Window *window : std::as_const(m_windows)) { 1905 X11Window *x11Window = qobject_cast<X11Window *>(window); 1906 if (x11Window && !x11Window->isUnmanaged() && func(x11Window)) { 1907 return x11Window; 1908 } 1909 } 1910 return nullptr; 1911 } 1912 1913 X11Window *Workspace::findUnmanaged(std::function<bool(const X11Window *)> func) const 1914 { 1915 for (Window *window : m_windows) { 1916 X11Window *x11Window = qobject_cast<X11Window *>(window); 1917 if (x11Window && x11Window->isUnmanaged() && func(x11Window)) { 1918 return x11Window; 1919 } 1920 } 1921 return nullptr; 1922 } 1923 1924 X11Window *Workspace::findUnmanaged(xcb_window_t w) const 1925 { 1926 return findUnmanaged([w](const X11Window *u) { 1927 return u->window() == w; 1928 }); 1929 } 1930 1931 X11Window *Workspace::findClient(Predicate predicate, xcb_window_t w) const 1932 { 1933 switch (predicate) { 1934 case Predicate::WindowMatch: 1935 return findClient([w](const X11Window *c) { 1936 return c->window() == w; 1937 }); 1938 case Predicate::WrapperIdMatch: 1939 return findClient([w](const X11Window *c) { 1940 return c->wrapperId() == w; 1941 }); 1942 case Predicate::FrameIdMatch: 1943 return findClient([w](const X11Window *c) { 1944 return c->frameId() == w; 1945 }); 1946 case Predicate::InputIdMatch: 1947 return findClient([w](const X11Window *c) { 1948 return c->inputId() == w; 1949 }); 1950 } 1951 return nullptr; 1952 } 1953 1954 Window *Workspace::findWindow(std::function<bool(const Window *)> func) const 1955 { 1956 return Window::findInList(m_windows, func); 1957 } 1958 1959 Window *Workspace::findWindow(const QUuid &internalId) const 1960 { 1961 return findWindow([internalId](const KWin::Window *l) -> bool { 1962 return internalId == l->internalId(); 1963 }); 1964 } 1965 1966 void Workspace::forEachWindow(std::function<void(Window *)> func) 1967 { 1968 std::for_each(m_windows.constBegin(), m_windows.constEnd(), func); 1969 } 1970 1971 bool Workspace::hasWindow(const Window *c) 1972 { 1973 return findWindow([&c](const Window *test) { 1974 return test == c; 1975 }) 1976 != nullptr; 1977 } 1978 1979 Window *Workspace::findInternal(QWindow *w) const 1980 { 1981 if (!w) { 1982 return nullptr; 1983 } 1984 if (kwinApp()->operationMode() == Application::OperationModeX11) { 1985 return findUnmanaged(w->winId()); 1986 } 1987 for (Window *window : m_windows) { 1988 if (InternalWindow *internal = qobject_cast<InternalWindow *>(window)) { 1989 if (internal->handle() == w) { 1990 return internal; 1991 } 1992 } 1993 } 1994 return nullptr; 1995 } 1996 1997 void Workspace::setWasUserInteraction() 1998 { 1999 if (was_user_interaction) { 2000 return; 2001 } 2002 was_user_interaction = true; 2003 // might be called from within the filter, so delay till we now the filter returned 2004 QTimer::singleShot(0, this, 2005 [this] { 2006 m_wasUserInteractionFilter.reset(); 2007 }); 2008 } 2009 2010 void Workspace::updateTabbox() 2011 { 2012 #if KWIN_BUILD_TABBOX 2013 // Need to reset the client model even if the task switcher is hidden otherwise there 2014 // might be dangling pointers. Consider rewriting client model logic! 2015 m_tabbox->reset(true); 2016 #endif 2017 } 2018 2019 void Workspace::addInternalWindow(InternalWindow *window) 2020 { 2021 Q_ASSERT(!m_windows.contains(window)); 2022 m_windows.append(window); 2023 addToStack(window); 2024 2025 setupWindowConnections(window); 2026 window->updateLayer(); 2027 2028 if (window->isPlaceable()) { 2029 const QRectF area = clientArea(PlacementArea, window, workspace()->activeOutput()); 2030 m_placement->place(window, area); 2031 } 2032 2033 updateStackingOrder(true); 2034 Q_EMIT windowAdded(window); 2035 } 2036 2037 void Workspace::removeInternalWindow(InternalWindow *window) 2038 { 2039 m_windows.removeOne(window); 2040 2041 updateStackingOrder(); 2042 Q_EMIT windowRemoved(window); 2043 } 2044 2045 void Workspace::setInitialDesktop(int desktop) 2046 { 2047 m_initialDesktop = desktop; 2048 } 2049 2050 Group *Workspace::findGroup(xcb_window_t leader) const 2051 { 2052 Q_ASSERT(leader != XCB_WINDOW_NONE); 2053 for (auto it = groups.constBegin(); it != groups.constEnd(); ++it) { 2054 if ((*it)->leader() == leader) { 2055 return *it; 2056 } 2057 } 2058 return nullptr; 2059 } 2060 2061 // Window is group transient, but has no group set. Try to find 2062 // group with windows with the same client leader. 2063 Group *Workspace::findClientLeaderGroup(const X11Window *window) const 2064 { 2065 Group *ret = nullptr; 2066 for (auto it = m_windows.constBegin(); it != m_windows.constEnd(); ++it) { 2067 X11Window *candidate = qobject_cast<X11Window *>(*it); 2068 if (!candidate || candidate == window) { 2069 continue; 2070 } 2071 if (candidate->wmClientLeader() == window->wmClientLeader()) { 2072 if (ret == nullptr || ret == candidate->group()) { 2073 ret = candidate->group(); 2074 } else { 2075 // There are already two groups with the same client leader. 2076 // This most probably means the app uses group transients without 2077 // setting group for its windows. Merging the two groups is a bad 2078 // hack, but there's no really good solution for this case. 2079 QList<X11Window *> old_group = candidate->group()->members(); 2080 // old_group autodeletes when being empty 2081 for (int pos = 0; pos < old_group.count(); ++pos) { 2082 X11Window *tmp = old_group[pos]; 2083 if (tmp != window) { 2084 tmp->changeClientLeaderGroup(ret); 2085 } 2086 } 2087 } 2088 } 2089 } 2090 return ret; 2091 } 2092 2093 void Workspace::updateMinimizedOfTransients(Window *window) 2094 { 2095 // if mainwindow is minimized or shaded, minimize transients too 2096 if (window->isMinimized()) { 2097 for (auto it = window->transients().constBegin(); it != window->transients().constEnd(); ++it) { 2098 if ((*it)->isModal()) { 2099 continue; // there's no reason to hide modal dialogs with the main window 2100 } 2101 // but to keep them to eg. watch progress or whatever 2102 if (!(*it)->isMinimized()) { 2103 (*it)->setMinimized(true); 2104 updateMinimizedOfTransients((*it)); 2105 } 2106 } 2107 if (window->isModal()) { // if a modal dialog is minimized, minimize its mainwindow too 2108 const auto windows = window->mainWindows(); 2109 for (Window *main : std::as_const(windows)) { 2110 main->setMinimized(true); 2111 } 2112 } 2113 } else { 2114 // else unmiminize the transients 2115 for (auto it = window->transients().constBegin(); it != window->transients().constEnd(); ++it) { 2116 if ((*it)->isMinimized()) { 2117 (*it)->setMinimized(false); 2118 updateMinimizedOfTransients((*it)); 2119 } 2120 } 2121 if (window->isModal()) { 2122 const auto windows = window->mainWindows(); 2123 for (Window *main : std::as_const(windows)) { 2124 main->setMinimized(false); 2125 } 2126 } 2127 } 2128 } 2129 2130 /** 2131 * Sets the \a window's transient windows' on_all_desktops property to \a on_all_desktops. 2132 */ 2133 void Workspace::updateOnAllDesktopsOfTransients(Window *window) 2134 { 2135 for (auto it = window->transients().constBegin(); it != window->transients().constEnd(); ++it) { 2136 if ((*it)->isOnAllDesktops() != window->isOnAllDesktops()) { 2137 (*it)->setOnAllDesktops(window->isOnAllDesktops()); 2138 } 2139 } 2140 } 2141 2142 // A new window has been mapped. Check if it's not a mainwindow for some already existing transient window. 2143 void Workspace::checkTransients(xcb_window_t w) 2144 { 2145 for (auto it = m_windows.constBegin(); it != m_windows.constEnd(); ++it) { 2146 if (X11Window *x11Window = qobject_cast<X11Window *>(*it)) { 2147 x11Window->checkTransient(w); 2148 } 2149 } 2150 } 2151 2152 /** 2153 * Resizes the workspace after an XRANDR screen size change 2154 */ 2155 void Workspace::desktopResized() 2156 { 2157 m_placementTracker->inhibit(); 2158 2159 const QRect oldGeometry = m_geometry; 2160 m_geometry = QRect(); 2161 for (const Output *output : std::as_const(m_outputs)) { 2162 m_geometry = m_geometry.united(output->geometry()); 2163 } 2164 2165 if (rootInfo()) { 2166 NETSize desktop_geometry; 2167 desktop_geometry.width = Xcb::toXNative(m_geometry.width()); 2168 desktop_geometry.height = Xcb::toXNative(m_geometry.height()); 2169 rootInfo()->setDesktopGeometry(desktop_geometry); 2170 } 2171 2172 updateClientArea(); 2173 2174 const auto stack = stackingOrder(); 2175 for (Window *window : stack) { 2176 window->setMoveResizeOutput(outputAt(window->moveResizeGeometry().center())); 2177 window->setOutput(outputAt(window->frameGeometry().center())); 2178 } 2179 2180 // restore cursor position 2181 const auto oldCursorOutput = std::find_if(m_oldScreenGeometries.cbegin(), m_oldScreenGeometries.cend(), [](const auto &geometry) { 2182 return exclusiveContains(geometry, Cursors::self()->mouse()->pos()); 2183 }); 2184 if (oldCursorOutput != m_oldScreenGeometries.cend()) { 2185 const Output *cursorOutput = oldCursorOutput.key(); 2186 if (std::find(m_outputs.cbegin(), m_outputs.cend(), cursorOutput) != m_outputs.cend()) { 2187 const QRect oldGeometry = oldCursorOutput.value(); 2188 const QRect newGeometry = cursorOutput->geometry(); 2189 const QPointF relativePosition = Cursors::self()->mouse()->pos() - oldGeometry.topLeft(); 2190 const QPointF newRelativePosition(newGeometry.width() * relativePosition.x() / float(oldGeometry.width()), newGeometry.height() * relativePosition.y() / float(oldGeometry.height())); 2191 Cursors::self()->mouse()->setPos(newGeometry.topLeft() + newRelativePosition); 2192 } 2193 } 2194 2195 saveOldScreenSizes(); // after updateClientArea(), so that one still uses the previous one 2196 2197 // TODO: emit a signal instead and remove the deep function calls into edges and effects 2198 m_screenEdges->recreateEdges(); 2199 2200 m_placementTracker->uninhibit(); 2201 m_placementTracker->restore(getPlacementTrackerHash()); 2202 if (m_geometry != oldGeometry) { 2203 Q_EMIT geometryChanged(); 2204 } 2205 } 2206 2207 void Workspace::saveOldScreenSizes() 2208 { 2209 m_oldScreenGeometries.clear(); 2210 for (const Output *output : std::as_const(m_outputs)) { 2211 m_oldScreenGeometries.insert(output, output->geometry()); 2212 } 2213 } 2214 2215 /** 2216 * Whether or not the window has a strut that expands through the invisible area of 2217 * an xinerama setup where the monitors are not the same resolution. 2218 */ 2219 static bool hasOffscreenXineramaStrut(Window *window) 2220 { 2221 if (qobject_cast<X11Window *>(window)) { 2222 // Get strut as a QRegion 2223 QRegion region; 2224 region += window->strutRect(StrutAreaTop); 2225 region += window->strutRect(StrutAreaRight); 2226 region += window->strutRect(StrutAreaBottom); 2227 region += window->strutRect(StrutAreaLeft); 2228 2229 // Remove all visible areas so that only the invisible remain 2230 const auto outputs = workspace()->outputs(); 2231 for (const Output *output : outputs) { 2232 region -= output->geometry(); 2233 } 2234 2235 // If there's anything left then we have an offscreen strut 2236 return !region.isEmpty(); 2237 } 2238 2239 return false; 2240 } 2241 2242 QRectF Workspace::adjustClientArea(Window *window, const QRectF &area) const 2243 { 2244 QRectF adjustedArea = area; 2245 2246 QRectF strutLeft = window->strutRect(StrutAreaLeft); 2247 QRectF strutRight = window->strutRect(StrutAreaRight); 2248 QRectF strutTop = window->strutRect(StrutAreaTop); 2249 QRectF strutBottom = window->strutRect(StrutAreaBottom); 2250 2251 QRectF screenArea = clientArea(ScreenArea, window); 2252 if (qobject_cast<X11Window *>(window)) { 2253 // HACK: workarea handling is not xinerama aware, so if this strut 2254 // reserves place at a xinerama edge that's inside the virtual screen, 2255 // ignore the strut for workspace setting. 2256 if (area == QRect(QPoint(0, 0), m_geometry.size())) { 2257 if (strutLeft.left() < screenArea.left()) { 2258 strutLeft = QRect(); 2259 } 2260 if (strutRight.right() > screenArea.right()) { 2261 strutRight = QRect(); 2262 } 2263 if (strutTop.top() < screenArea.top()) { 2264 strutTop = QRect(); 2265 } 2266 if (strutBottom.bottom() < screenArea.bottom()) { 2267 strutBottom = QRect(); 2268 } 2269 } 2270 } 2271 2272 // Handle struts at xinerama edges that are inside the virtual screen. 2273 // They're given in virtual screen coordinates, make them affect only 2274 // their xinerama screen. 2275 strutLeft.setLeft(std::max(strutLeft.left(), screenArea.left())); 2276 strutRight.setRight(std::min(strutRight.right(), screenArea.right())); 2277 strutTop.setTop(std::max(strutTop.top(), screenArea.top())); 2278 strutBottom.setBottom(std::min(strutBottom.bottom(), screenArea.bottom())); 2279 2280 if (strutLeft.intersects(area)) { 2281 adjustedArea.setLeft(strutLeft.right()); 2282 } 2283 if (strutRight.intersects(area)) { 2284 adjustedArea.setRight(strutRight.left()); 2285 } 2286 if (strutTop.intersects(area)) { 2287 adjustedArea.setTop(strutTop.bottom()); 2288 } 2289 if (strutBottom.intersects(area)) { 2290 adjustedArea.setBottom(strutBottom.top()); 2291 } 2292 2293 return adjustedArea; 2294 } 2295 2296 /** 2297 * Updates the current client areas according to the current windows. 2298 * 2299 * The client area is the area that is available for windows (that 2300 * which is not taken by windows like panels, the top-of-screen menu 2301 * etc). 2302 * 2303 * @see clientArea() 2304 */ 2305 void Workspace::updateClientArea() 2306 { 2307 const QList<VirtualDesktop *> desktops = VirtualDesktopManager::self()->desktops(); 2308 2309 QHash<const VirtualDesktop *, QRectF> workAreas; 2310 QHash<const VirtualDesktop *, StrutRects> restrictedAreas; 2311 QHash<const VirtualDesktop *, QHash<const Output *, QRectF>> screenAreas; 2312 2313 for (const VirtualDesktop *desktop : desktops) { 2314 workAreas[desktop] = m_geometry; 2315 2316 for (const Output *output : std::as_const(m_outputs)) { 2317 screenAreas[desktop][output] = output->geometryF(); 2318 } 2319 } 2320 2321 for (Window *window : std::as_const(m_windows)) { 2322 if (!window->hasStrut()) { 2323 continue; 2324 } 2325 QRectF r = adjustClientArea(window, m_geometry); 2326 2327 // This happens sometimes when the workspace size changes and the 2328 // struted windows haven't repositioned yet 2329 if (!r.isValid()) { 2330 continue; 2331 } 2332 // sanity check that a strut doesn't exclude a complete screen geometry 2333 // this is a violation to EWMH, as KWin just ignores the strut 2334 for (const Output *output : std::as_const(m_outputs)) { 2335 if (!r.intersects(output->geometry())) { 2336 qCDebug(KWIN_CORE) << "Adjusted client area would exclude a complete screen, ignore"; 2337 r = m_geometry; 2338 break; 2339 } 2340 } 2341 StrutRects strutRegion = window->strutRects(); 2342 const QRect clientsScreenRect = window->output()->geometry(); 2343 for (int i = strutRegion.size() - 1; i >= 0; --i) { 2344 const StrutRect clipped = StrutRect(strutRegion[i].intersected(clientsScreenRect), strutRegion[i].area()); 2345 if (clipped.isEmpty()) { 2346 strutRegion.removeAt(i); 2347 } else { 2348 strutRegion[i] = clipped; 2349 } 2350 } 2351 2352 // Ignore offscreen xinerama struts. These interfere with the larger monitors on the setup 2353 // and should be ignored so that applications that use the work area to work out where 2354 // windows can go can use the entire visible area of the larger monitors. 2355 // This goes against the EWMH description of the work area but it is a toss up between 2356 // having unusable sections of the screen (Which can be quite large with newer monitors) 2357 // or having some content appear offscreen (Relatively rare compared to other). 2358 bool hasOffscreenStrut = hasOffscreenXineramaStrut(window); 2359 2360 const auto vds = window->isOnAllDesktops() ? desktops : window->desktops(); 2361 for (VirtualDesktop *vd : vds) { 2362 if (!hasOffscreenStrut) { 2363 workAreas[vd] &= r; 2364 } 2365 restrictedAreas[vd] += strutRegion; 2366 for (Output *output : std::as_const(m_outputs)) { 2367 const auto geo = screenAreas[vd][output].intersected(adjustClientArea(window, output->geometryF())); 2368 // ignore the geometry if it results in the screen getting removed completely 2369 if (!geo.isEmpty()) { 2370 screenAreas[vd][output] = geo; 2371 } 2372 } 2373 } 2374 } 2375 2376 if (m_workAreas != workAreas || m_restrictedAreas != restrictedAreas || m_screenAreas != screenAreas) { 2377 m_workAreas = workAreas; 2378 m_screenAreas = screenAreas; 2379 2380 m_inUpdateClientArea = true; 2381 m_oldRestrictedAreas = m_restrictedAreas; 2382 m_restrictedAreas = restrictedAreas; 2383 2384 if (rootInfo()) { 2385 for (VirtualDesktop *desktop : desktops) { 2386 const QRectF &workArea = m_workAreas[desktop]; 2387 NETRect r(Xcb::toXNative(workArea)); 2388 rootInfo()->setWorkArea(desktop->x11DesktopNumber(), r); 2389 } 2390 } 2391 2392 for (auto it = m_windows.constBegin(); it != m_windows.constEnd(); ++it) { 2393 if ((*it)->isClient()) { 2394 (*it)->checkWorkspacePosition(); 2395 } 2396 } 2397 2398 m_oldRestrictedAreas.clear(); // reset, no longer valid or needed 2399 m_inUpdateClientArea = false; 2400 } 2401 } 2402 2403 /** 2404 * Returns the area available for windows. This is the desktop 2405 * geometry minus windows on the dock. Placement algorithms should 2406 * refer to this rather than Screens::geometry. 2407 */ 2408 QRectF Workspace::clientArea(clientAreaOption opt, const Output *output, const VirtualDesktop *desktop) const 2409 { 2410 switch (opt) { 2411 case MaximizeArea: 2412 case PlacementArea: 2413 if (auto desktopIt = m_screenAreas.constFind(desktop); desktopIt != m_screenAreas.constEnd()) { 2414 if (auto outputIt = desktopIt->constFind(output); outputIt != desktopIt->constEnd()) { 2415 return *outputIt; 2416 } 2417 } 2418 return output->geometryF(); 2419 case MaximizeFullArea: 2420 case FullScreenArea: 2421 case MovementArea: 2422 case ScreenArea: 2423 return output->geometryF(); 2424 case WorkArea: 2425 return m_workAreas.value(desktop, m_geometry); 2426 case FullArea: 2427 return m_geometry; 2428 default: 2429 Q_UNREACHABLE(); 2430 } 2431 } 2432 2433 QRectF Workspace::clientArea(clientAreaOption opt, const Window *window) const 2434 { 2435 return clientArea(opt, window, window->output()); 2436 } 2437 2438 QRectF Workspace::clientArea(clientAreaOption opt, const Window *window, const Output *output) const 2439 { 2440 const VirtualDesktop *desktop; 2441 if (window->isOnCurrentDesktop()) { 2442 desktop = VirtualDesktopManager::self()->currentDesktop(); 2443 } else { 2444 desktop = window->desktops().constLast(); 2445 } 2446 return clientArea(opt, output, desktop); 2447 } 2448 2449 QRectF Workspace::clientArea(clientAreaOption opt, const Window *window, const QPointF &pos) const 2450 { 2451 return clientArea(opt, window, outputAt(pos)); 2452 } 2453 2454 QRect Workspace::geometry() const 2455 { 2456 return m_geometry; 2457 } 2458 2459 StrutRects Workspace::restrictedMoveArea(const VirtualDesktop *desktop, StrutAreas areas) const 2460 { 2461 const StrutRects strut = m_restrictedAreas.value(desktop); 2462 if (areas == StrutAreaAll) { 2463 return strut; 2464 } 2465 2466 StrutRects ret; 2467 ret.reserve(strut.size()); 2468 for (const StrutRect &rect : strut) { 2469 if (rect.area() & areas) { 2470 ret.append(rect); 2471 } 2472 } 2473 return ret; 2474 } 2475 2476 bool Workspace::inUpdateClientArea() const 2477 { 2478 return m_inUpdateClientArea; 2479 } 2480 2481 StrutRects Workspace::previousRestrictedMoveArea(const VirtualDesktop *desktop, StrutAreas areas) const 2482 { 2483 const StrutRects strut = m_oldRestrictedAreas.value(desktop); 2484 if (areas == StrutAreaAll) { 2485 return strut; 2486 } 2487 2488 StrutRects ret; 2489 ret.reserve(strut.size()); 2490 for (const StrutRect &rect : strut) { 2491 if (rect.area() & areas) { 2492 ret.append(rect); 2493 } 2494 } 2495 return ret; 2496 } 2497 2498 QHash<const Output *, QRect> Workspace::previousScreenSizes() const 2499 { 2500 return m_oldScreenGeometries; 2501 } 2502 2503 Output *Workspace::xineramaIndexToOutput(int index) const 2504 { 2505 xcb_connection_t *connection = kwinApp()->x11Connection(); 2506 if (!connection) { 2507 return nullptr; 2508 } 2509 2510 const UniqueCPtr<xcb_xinerama_is_active_reply_t> active{xcb_xinerama_is_active_reply(connection, xcb_xinerama_is_active(connection), nullptr)}; 2511 if (!active || !active->state) { 2512 return nullptr; 2513 } 2514 2515 const UniqueCPtr<xcb_xinerama_query_screens_reply_t> screens(xcb_xinerama_query_screens_reply(connection, xcb_xinerama_query_screens(connection), nullptr)); 2516 if (!screens) { 2517 return nullptr; 2518 } 2519 2520 const int infoCount = xcb_xinerama_query_screens_screen_info_length(screens.get()); 2521 if (index >= infoCount) { 2522 return nullptr; 2523 } 2524 2525 const xcb_xinerama_screen_info_t *infos = xcb_xinerama_query_screens_screen_info(screens.get()); 2526 const QRect needle(infos[index].x_org, infos[index].y_org, infos[index].width, infos[index].height); 2527 2528 for (Output *output : std::as_const(m_outputs)) { 2529 if (Xcb::toXNative(output->geometryF()) == needle) { 2530 return output; 2531 } 2532 } 2533 2534 return nullptr; 2535 } 2536 2537 void Workspace::setOutputOrder(const QList<Output *> &order) 2538 { 2539 if (m_outputOrder != order) { 2540 m_outputOrder = order; 2541 Q_EMIT outputOrderChanged(); 2542 } 2543 } 2544 2545 QList<Output *> Workspace::outputOrder() const 2546 { 2547 return m_outputOrder; 2548 } 2549 2550 Output *Workspace::activeOutput() const 2551 { 2552 if (options->activeMouseScreen()) { 2553 if (m_activeCursorOutput) { 2554 return m_activeCursorOutput; 2555 } else { 2556 return outputAt(Cursors::self()->mouse()->pos()); 2557 } 2558 } 2559 2560 if (m_activeWindow && !m_activeWindow->isOnOutput(m_activeOutput)) { 2561 return m_activeWindow->output(); 2562 } 2563 2564 return m_activeOutput; 2565 } 2566 2567 void Workspace::setActiveOutput(Output *output) 2568 { 2569 m_activeOutput = output; 2570 } 2571 2572 void Workspace::setActiveOutput(const QPointF &pos) 2573 { 2574 setActiveOutput(outputAt(pos)); 2575 } 2576 2577 void Workspace::setActiveCursorOutput(Output *output) 2578 { 2579 m_activeCursorOutput = output; 2580 } 2581 2582 void Workspace::setActiveCursorOutput(const QPointF &pos) 2583 { 2584 setActiveCursorOutput(outputAt(pos)); 2585 } 2586 2587 /** 2588 * \a window is moved around to position \a pos. This gives the 2589 * workspace the opportunity to interveniate and to implement 2590 * snap-to-windows functionality. 2591 * 2592 * The parameter \a snapAdjust is a multiplier used to calculate the 2593 * effective snap zones. When 1.0, it means that the snap zones will be 2594 * used without change. 2595 */ 2596 QPointF Workspace::adjustWindowPosition(Window *window, QPointF pos, bool unrestricted, double snapAdjust) 2597 { 2598 QSizeF borderSnapZone(options->borderSnapZone(), options->borderSnapZone()); 2599 QRectF maxRect; 2600 int guideMaximized = MaximizeRestore; 2601 if (window->maximizeMode() != MaximizeRestore) { 2602 maxRect = clientArea(MaximizeArea, window, pos + window->rect().center()); 2603 QRectF geo = window->frameGeometry(); 2604 if (window->maximizeMode() & MaximizeHorizontal && (geo.x() == maxRect.left() || geo.right() == maxRect.right())) { 2605 guideMaximized |= MaximizeHorizontal; 2606 borderSnapZone.setWidth(std::max(borderSnapZone.width() + 2, maxRect.width() / 16)); 2607 } 2608 if (window->maximizeMode() & MaximizeVertical && (geo.y() == maxRect.top() || geo.bottom() == maxRect.bottom())) { 2609 guideMaximized |= MaximizeVertical; 2610 borderSnapZone.setHeight(std::max(borderSnapZone.height() + 2, maxRect.height() / 16)); 2611 } 2612 } 2613 2614 if (options->windowSnapZone() || !borderSnapZone.isNull() || options->centerSnapZone()) { 2615 2616 const bool sOWO = options->isSnapOnlyWhenOverlapping(); 2617 const Output *output = outputAt(pos + window->rect().center()); 2618 if (maxRect.isNull()) { 2619 maxRect = clientArea(MaximizeArea, window, output); 2620 } 2621 const int xmin = maxRect.left(); 2622 const int xmax = maxRect.right(); // desk size 2623 const int ymin = maxRect.top(); 2624 const int ymax = maxRect.bottom(); 2625 2626 const int cx(pos.x()); 2627 const int cy(pos.y()); 2628 const int cw(window->width()); 2629 const int ch(window->height()); 2630 const int rx(cx + cw); 2631 const int ry(cy + ch); // these don't change 2632 2633 int nx(cx), ny(cy); // buffers 2634 int deltaX(xmax); 2635 int deltaY(ymax); // minimum distance to other windows 2636 2637 int lx, ly, lrx, lry; // coords and size for the comparison window, l 2638 2639 // border snap 2640 const int borderXSnapZone = borderSnapZone.width() * snapAdjust; // snap trigger 2641 const int borderYSnapZone = borderSnapZone.height() * snapAdjust; 2642 if (borderXSnapZone > 0 || borderYSnapZone > 0) { 2643 if ((sOWO ? (cx < xmin) : true) && (std::abs(xmin - cx) < borderXSnapZone)) { 2644 deltaX = xmin - cx; 2645 nx = xmin; 2646 } 2647 if ((sOWO ? (rx > xmax) : true) && (std::abs(rx - xmax) < borderXSnapZone) && (std::abs(xmax - rx) < deltaX)) { 2648 deltaX = rx - xmax; 2649 nx = xmax - cw; 2650 } 2651 2652 if ((sOWO ? (cy < ymin) : true) && (std::abs(ymin - cy) < borderYSnapZone)) { 2653 deltaY = ymin - cy; 2654 ny = ymin; 2655 } 2656 if ((sOWO ? (ry > ymax) : true) && (std::abs(ry - ymax) < borderYSnapZone) && (std::abs(ymax - ry) < deltaY)) { 2657 deltaY = ry - ymax; 2658 ny = ymax - ch; 2659 } 2660 } 2661 2662 // windows snap 2663 const int windowSnapZone = options->windowSnapZone() * snapAdjust; 2664 if (windowSnapZone > 0) { 2665 for (auto l = m_windows.constBegin(); l != m_windows.constEnd(); ++l) { 2666 if ((*l) == window) { 2667 continue; 2668 } 2669 if ((*l)->isMinimized()) { 2670 continue; 2671 } 2672 if (!(*l)->isShown()) { 2673 continue; 2674 } 2675 if (!(*l)->isOnCurrentDesktop()) { 2676 continue; // wrong virtual desktop 2677 } 2678 if (!(*l)->isOnCurrentActivity()) { 2679 continue; // wrong activity 2680 } 2681 if ((*l)->isUnmanaged() || (*l)->isDesktop() || (*l)->isSplash() || (*l)->isNotification() || (*l)->isCriticalNotification() || (*l)->isOnScreenDisplay() || (*l)->isAppletPopup()) { 2682 continue; 2683 } 2684 2685 lx = (*l)->x(); 2686 ly = (*l)->y(); 2687 lrx = lx + (*l)->width(); 2688 lry = ly + (*l)->height(); 2689 2690 if (!(guideMaximized & MaximizeHorizontal) && (((cy <= lry) && (cy >= ly)) || ((ry >= ly) && (ry <= lry)) || ((cy <= ly) && (ry >= lry)))) { 2691 if ((sOWO ? (cx < lrx) : true) && (std::abs(lrx - cx) < windowSnapZone) && (std::abs(lrx - cx) < deltaX)) { 2692 deltaX = std::abs(lrx - cx); 2693 nx = lrx; 2694 } 2695 if ((sOWO ? (rx > lx) : true) && (std::abs(rx - lx) < windowSnapZone) && (std::abs(rx - lx) < deltaX)) { 2696 deltaX = std::abs(rx - lx); 2697 nx = lx - cw; 2698 } 2699 } 2700 2701 if (!(guideMaximized & MaximizeVertical) && (((cx <= lrx) && (cx >= lx)) || ((rx >= lx) && (rx <= lrx)) || ((cx <= lx) && (rx >= lrx)))) { 2702 if ((sOWO ? (cy < lry) : true) && (std::abs(lry - cy) < windowSnapZone) && (std::abs(lry - cy) < deltaY)) { 2703 deltaY = std::abs(lry - cy); 2704 ny = lry; 2705 } 2706 // if ( (std::abs( ry-ly ) < snap) && (std::abs( ry - ly ) < deltaY )) 2707 if ((sOWO ? (ry > ly) : true) && (std::abs(ry - ly) < windowSnapZone) && (std::abs(ry - ly) < deltaY)) { 2708 deltaY = std::abs(ry - ly); 2709 ny = ly - ch; 2710 } 2711 } 2712 2713 // Corner snapping 2714 if (!(guideMaximized & MaximizeVertical) && (nx == lrx || nx + cw == lx)) { 2715 if ((sOWO ? (ry > lry) : true) && (std::abs(lry - ry) < windowSnapZone) && (std::abs(lry - ry) < deltaY)) { 2716 deltaY = std::abs(lry - ry); 2717 ny = lry - ch; 2718 } 2719 if ((sOWO ? (cy < ly) : true) && (std::abs(cy - ly) < windowSnapZone) && (std::abs(cy - ly) < deltaY)) { 2720 deltaY = std::abs(cy - ly); 2721 ny = ly; 2722 } 2723 } 2724 if (!(guideMaximized & MaximizeHorizontal) && (ny == lry || ny + ch == ly)) { 2725 if ((sOWO ? (rx > lrx) : true) && (std::abs(lrx - rx) < windowSnapZone) && (std::abs(lrx - rx) < deltaX)) { 2726 deltaX = std::abs(lrx - rx); 2727 nx = lrx - cw; 2728 } 2729 if ((sOWO ? (cx < lx) : true) && (std::abs(cx - lx) < windowSnapZone) && (std::abs(cx - lx) < deltaX)) { 2730 deltaX = std::abs(cx - lx); 2731 nx = lx; 2732 } 2733 } 2734 } 2735 } 2736 2737 // center snap 2738 const int centerSnapZone = options->centerSnapZone() * snapAdjust; 2739 if (centerSnapZone > 0) { 2740 int diffX = std::abs((xmin + xmax) / 2 - (cx + cw / 2)); 2741 int diffY = std::abs((ymin + ymax) / 2 - (cy + ch / 2)); 2742 if (diffX < centerSnapZone && diffY < centerSnapZone && diffX < deltaX && diffY < deltaY) { 2743 // Snap to center of screen 2744 nx = (xmin + xmax) / 2 - cw / 2; 2745 ny = (ymin + ymax) / 2 - ch / 2; 2746 } else if (options->borderSnapZone() > 0) { 2747 // Enhance border snap 2748 if ((nx == xmin || nx == xmax - cw) && diffY < centerSnapZone && diffY < deltaY) { 2749 // Snap to vertical center on screen edge 2750 ny = (ymin + ymax) / 2 - ch / 2; 2751 } else if (((unrestricted ? ny == ymin : ny <= ymin) || ny == ymax - ch) && diffX < centerSnapZone && diffX < deltaX) { 2752 // Snap to horizontal center on screen edge 2753 nx = (xmin + xmax) / 2 - cw / 2; 2754 } 2755 } 2756 } 2757 2758 pos = QPoint(nx, ny); 2759 } 2760 return pos; 2761 } 2762 2763 QRectF Workspace::adjustWindowSize(Window *window, QRectF moveResizeGeom, Gravity gravity) 2764 { 2765 // adapted from adjustWindowPosition on 29May2004 2766 // this function is called when resizing a window and will modify 2767 // the new dimensions to snap to other windows/borders if appropriate 2768 if (options->windowSnapZone() || options->borderSnapZone()) { // || options->centerSnapZone ) 2769 const bool sOWO = options->isSnapOnlyWhenOverlapping(); 2770 2771 const QRectF maxRect = clientArea(MovementArea, window, window->rect().center()); 2772 const qreal xmin = maxRect.left(); 2773 const qreal xmax = maxRect.right(); // desk size 2774 const qreal ymin = maxRect.top(); 2775 const qreal ymax = maxRect.bottom(); 2776 2777 const qreal cx(moveResizeGeom.left()); 2778 const qreal cy(moveResizeGeom.top()); 2779 const qreal rx(moveResizeGeom.right()); 2780 const qreal ry(moveResizeGeom.bottom()); 2781 2782 qreal newcx(cx), newcy(cy); // buffers 2783 qreal newrx(rx), newry(ry); 2784 qreal deltaX(xmax); 2785 qreal deltaY(ymax); // minimum distance to other windows 2786 2787 qreal lx, ly, lrx, lry; // coords and size for the comparison window, l 2788 2789 // border snap 2790 int snap = options->borderSnapZone(); // snap trigger 2791 if (snap) { 2792 deltaX = int(snap); 2793 deltaY = int(snap); 2794 2795 #define SNAP_BORDER_TOP \ 2796 if ((sOWO ? (newcy < ymin) : true) && (std::abs(ymin - newcy) < deltaY)) { \ 2797 deltaY = std::abs(ymin - newcy); \ 2798 newcy = ymin; \ 2799 } 2800 2801 #define SNAP_BORDER_BOTTOM \ 2802 if ((sOWO ? (newry > ymax) : true) && (std::abs(ymax - newry) < deltaY)) { \ 2803 deltaY = std::abs(ymax - newcy); \ 2804 newry = ymax; \ 2805 } 2806 2807 #define SNAP_BORDER_LEFT \ 2808 if ((sOWO ? (newcx < xmin) : true) && (std::abs(xmin - newcx) < deltaX)) { \ 2809 deltaX = std::abs(xmin - newcx); \ 2810 newcx = xmin; \ 2811 } 2812 2813 #define SNAP_BORDER_RIGHT \ 2814 if ((sOWO ? (newrx > xmax) : true) && (std::abs(xmax - newrx) < deltaX)) { \ 2815 deltaX = std::abs(xmax - newrx); \ 2816 newrx = xmax; \ 2817 } 2818 switch (gravity) { 2819 case Gravity::BottomRight: 2820 SNAP_BORDER_BOTTOM 2821 SNAP_BORDER_RIGHT 2822 break; 2823 case Gravity::Right: 2824 SNAP_BORDER_RIGHT 2825 break; 2826 case Gravity::Bottom: 2827 SNAP_BORDER_BOTTOM 2828 break; 2829 case Gravity::TopLeft: 2830 SNAP_BORDER_TOP 2831 SNAP_BORDER_LEFT 2832 break; 2833 case Gravity::Left: 2834 SNAP_BORDER_LEFT 2835 break; 2836 case Gravity::Top: 2837 SNAP_BORDER_TOP 2838 break; 2839 case Gravity::TopRight: 2840 SNAP_BORDER_TOP 2841 SNAP_BORDER_RIGHT 2842 break; 2843 case Gravity::BottomLeft: 2844 SNAP_BORDER_BOTTOM 2845 SNAP_BORDER_LEFT 2846 break; 2847 default: 2848 Q_UNREACHABLE(); 2849 break; 2850 } 2851 } 2852 2853 // windows snap 2854 snap = options->windowSnapZone(); 2855 if (snap) { 2856 deltaX = int(snap); 2857 deltaY = int(snap); 2858 for (auto l = m_windows.constBegin(); l != m_windows.constEnd(); ++l) { 2859 if ((*l)->isOnCurrentDesktop() && !(*l)->isMinimized() && !(*l)->isUnmanaged() 2860 && (*l) != window) { 2861 lx = (*l)->x(); 2862 ly = (*l)->y(); 2863 lrx = (*l)->x() + (*l)->width(); 2864 lry = (*l)->y() + (*l)->height(); 2865 2866 #define WITHIN_HEIGHT (((newcy <= lry) && (newcy >= ly)) || ((newry >= ly) && (newry <= lry)) || ((newcy <= ly) && (newry >= lry))) 2867 2868 #define WITHIN_WIDTH (((cx <= lrx) && (cx >= lx)) || ((rx >= lx) && (rx <= lrx)) || ((cx <= lx) && (rx >= lrx))) 2869 2870 #define SNAP_WINDOW_TOP \ 2871 if ((sOWO ? (newcy < lry) : true) \ 2872 && WITHIN_WIDTH \ 2873 && (std::abs(lry - newcy) < deltaY)) { \ 2874 deltaY = std::abs(lry - newcy); \ 2875 newcy = lry; \ 2876 } 2877 2878 #define SNAP_WINDOW_BOTTOM \ 2879 if ((sOWO ? (newry > ly) : true) \ 2880 && WITHIN_WIDTH \ 2881 && (std::abs(ly - newry) < deltaY)) { \ 2882 deltaY = std::abs(ly - newry); \ 2883 newry = ly; \ 2884 } 2885 2886 #define SNAP_WINDOW_LEFT \ 2887 if ((sOWO ? (newcx < lrx) : true) \ 2888 && WITHIN_HEIGHT \ 2889 && (std::abs(lrx - newcx) < deltaX)) { \ 2890 deltaX = std::abs(lrx - newcx); \ 2891 newcx = lrx; \ 2892 } 2893 2894 #define SNAP_WINDOW_RIGHT \ 2895 if ((sOWO ? (newrx > lx) : true) \ 2896 && WITHIN_HEIGHT \ 2897 && (std::abs(lx - newrx) < deltaX)) { \ 2898 deltaX = std::abs(lx - newrx); \ 2899 newrx = lx; \ 2900 } 2901 2902 #define SNAP_WINDOW_C_TOP \ 2903 if ((sOWO ? (newcy < ly) : true) \ 2904 && (newcx == lrx || newrx == lx) \ 2905 && std::abs(ly - newcy) < deltaY) { \ 2906 deltaY = std::abs(ly - newcy); \ 2907 newcy = ly; \ 2908 } 2909 2910 #define SNAP_WINDOW_C_BOTTOM \ 2911 if ((sOWO ? (newry > lry) : true) \ 2912 && (newcx == lrx || newrx == lx) \ 2913 && std::abs(lry - newry) < deltaY) { \ 2914 deltaY = std::abs(lry - newry); \ 2915 newry = lry; \ 2916 } 2917 2918 #define SNAP_WINDOW_C_LEFT \ 2919 if ((sOWO ? (newcx < lx) : true) \ 2920 && (newcy == lry || newry == ly) \ 2921 && std::abs(lx - newcx) < deltaX) { \ 2922 deltaX = std::abs(lx - newcx); \ 2923 newcx = lx; \ 2924 } 2925 2926 #define SNAP_WINDOW_C_RIGHT \ 2927 if ((sOWO ? (newrx > lrx) : true) \ 2928 && (newcy == lry || newry == ly) \ 2929 && std::abs(lrx - newrx) < deltaX) { \ 2930 deltaX = std::abs(lrx - newrx); \ 2931 newrx = lrx; \ 2932 } 2933 2934 switch (gravity) { 2935 case Gravity::BottomRight: 2936 SNAP_WINDOW_BOTTOM 2937 SNAP_WINDOW_RIGHT 2938 SNAP_WINDOW_C_BOTTOM 2939 SNAP_WINDOW_C_RIGHT 2940 break; 2941 case Gravity::Right: 2942 SNAP_WINDOW_RIGHT 2943 SNAP_WINDOW_C_RIGHT 2944 break; 2945 case Gravity::Bottom: 2946 SNAP_WINDOW_BOTTOM 2947 SNAP_WINDOW_C_BOTTOM 2948 break; 2949 case Gravity::TopLeft: 2950 SNAP_WINDOW_TOP 2951 SNAP_WINDOW_LEFT 2952 SNAP_WINDOW_C_TOP 2953 SNAP_WINDOW_C_LEFT 2954 break; 2955 case Gravity::Left: 2956 SNAP_WINDOW_LEFT 2957 SNAP_WINDOW_C_LEFT 2958 break; 2959 case Gravity::Top: 2960 SNAP_WINDOW_TOP 2961 SNAP_WINDOW_C_TOP 2962 break; 2963 case Gravity::TopRight: 2964 SNAP_WINDOW_TOP 2965 SNAP_WINDOW_RIGHT 2966 SNAP_WINDOW_C_TOP 2967 SNAP_WINDOW_C_RIGHT 2968 break; 2969 case Gravity::BottomLeft: 2970 SNAP_WINDOW_BOTTOM 2971 SNAP_WINDOW_LEFT 2972 SNAP_WINDOW_C_BOTTOM 2973 SNAP_WINDOW_C_LEFT 2974 break; 2975 default: 2976 Q_UNREACHABLE(); 2977 break; 2978 } 2979 } 2980 } 2981 } 2982 2983 // center snap 2984 // snap = options->centerSnapZone; 2985 // if (snap) 2986 // { 2987 // // Don't resize snap to center as it interferes too much 2988 // // There are two ways of implementing this if wanted: 2989 // // 1) Snap only to the same points that the move snap does, and 2990 // // 2) Snap to the horizontal and vertical center lines of the screen 2991 // } 2992 2993 moveResizeGeom = QRectF(QPointF(newcx, newcy), QPointF(newrx, newry)); 2994 } 2995 return moveResizeGeom; 2996 } 2997 2998 /** 2999 * Marks the window as being moved or resized by the user. 3000 */ 3001 void Workspace::setMoveResizeWindow(Window *window) 3002 { 3003 Q_ASSERT(!window || !m_moveResizeWindow); // Catch attempts to move a second 3004 // window while still moving the first one. 3005 m_moveResizeWindow = window; 3006 if (m_moveResizeWindow) { 3007 ++block_focus; 3008 } else { 3009 --block_focus; 3010 } 3011 } 3012 3013 // When kwin crashes, windows will not be gravitated back to their original position 3014 // and will remain offset by the size of the decoration. So when restarting, fix this 3015 // (the property with the size of the frame remains on the window after the crash). 3016 void Workspace::fixPositionAfterCrash(xcb_window_t w, const xcb_get_geometry_reply_t *geometry) 3017 { 3018 NETWinInfo i(kwinApp()->x11Connection(), w, kwinApp()->x11RootWindow(), NET::WMFrameExtents, NET::Properties2()); 3019 NETStrut frame = i.frameExtents(); 3020 3021 if (frame.left != 0 || frame.top != 0) { 3022 // left and top needed due to narrowing conversations restrictions in C++11 3023 const uint32_t left = frame.left; 3024 const uint32_t top = frame.top; 3025 const uint32_t values[] = {Xcb::toXNative(geometry->x - left), Xcb::toXNative(geometry->y - top)}; 3026 xcb_configure_window(kwinApp()->x11Connection(), w, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, values); 3027 } 3028 } 3029 3030 FocusChain *Workspace::focusChain() const 3031 { 3032 return m_focusChain.get(); 3033 } 3034 3035 ApplicationMenu *Workspace::applicationMenu() const 3036 { 3037 return m_applicationMenu.get(); 3038 } 3039 3040 Decoration::DecorationBridge *Workspace::decorationBridge() const 3041 { 3042 return m_decorationBridge.get(); 3043 } 3044 3045 Outline *Workspace::outline() const 3046 { 3047 return m_outline.get(); 3048 } 3049 3050 Placement *Workspace::placement() const 3051 { 3052 return m_placement.get(); 3053 } 3054 3055 RuleBook *Workspace::rulebook() const 3056 { 3057 return m_rulebook.get(); 3058 } 3059 3060 ScreenEdges *Workspace::screenEdges() const 3061 { 3062 return m_screenEdges.get(); 3063 } 3064 3065 TileManager *Workspace::tileManager(Output *output) 3066 { 3067 return m_tileManagers.at(output).get(); 3068 } 3069 3070 #if KWIN_BUILD_TABBOX 3071 TabBox::TabBox *Workspace::tabbox() const 3072 { 3073 return m_tabbox.get(); 3074 } 3075 #endif 3076 3077 #if KWIN_BUILD_ACTIVITIES 3078 Activities *Workspace::activities() const 3079 { 3080 return m_activities.get(); 3081 } 3082 #endif 3083 3084 } // namespace 3085 3086 #include "moc_workspace.cpp"