File indexing completed on 2024-04-28 16:49:12

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