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