File indexing completed on 2024-04-28 05:30:38

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