File indexing completed on 2024-05-19 09:25:50

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