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