KWin
Loading...
Searching...
No Matches
workspace.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 1999, 2000 Matthias Ettrich <ettrich@kde.org>
6 SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
7 SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
8 SPDX-FileCopyrightText: 2022 Natalie Clarius <natalie_clarius@yahoo.de>
9
10 SPDX-License-Identifier: GPL-2.0-or-later
11*/
12// own
13#include "workspace.h"
14// kwin libs
15#include "opengl/glplatform.h"
16// kwin
17#include "core/output.h"
18#if KWIN_BUILD_ACTIVITIES
19#include "activities.h"
20#endif
21#include "appmenu.h"
22#include "atoms.h"
23#include "core/outputbackend.h"
25#include "cursor.h"
26#include "dbusinterface.h"
28#include "focuschain.h"
29#include "group.h"
30#include "input.h"
31#include "internalwindow.h"
32#include "killwindow.h"
34#include "netinfo.h"
35#include "outline.h"
36#include "placement.h"
37#include "pluginmanager.h"
38#include "rules.h"
39#include "screenedge.h"
40#include "scripting/scripting.h"
41#include "syncalarmx11filter.h"
42#include "tiles/tilemanager.h"
43#include "x11window.h"
44#if KWIN_BUILD_TABBOX
45#include "tabbox/tabbox.h"
46#endif
49#include "lidswitchtracker.h"
50#include "main.h"
53#include "placeholderoutput.h"
54#include "placementtracker.h"
55#include "tabletmodemanager.h"
56#include "tiles/tilemanager.h"
57#include "useractions.h"
58#include "utils/kernel.h"
60#include "utils/xcbutils.h"
61#include "virtualdesktops.h"
63#include "wayland_server.h"
64// KDE
65#include <KConfig>
66#include <KConfigGroup>
67#include <KLocalizedString>
68#include <KStartupInfo>
69// Qt
70#include <QCryptographicHash>
71#include <QDBusConnection>
72#include <QDBusPendingCall>
73#include <QMetaProperty>
74// xcb
75#include <xcb/xinerama.h>
76
77namespace KWin
78{
79
84
86{
87 return m_filter;
88}
89
90Workspace *Workspace::_self = nullptr;
91
93 : QObject(nullptr)
94 // Unsorted
95 , m_quickTileCombineTimer(nullptr)
96 , active_popup(nullptr)
97 , m_activePopupWindow(nullptr)
98 , m_initialDesktop(1)
99 , m_activeWindow(nullptr)
100 , m_lastActiveWindow(nullptr)
101 , m_moveResizeWindow(nullptr)
102 , m_delayFocusWindow(nullptr)
103 , force_restacking(false)
104 , showing_desktop(false)
105 , was_user_interaction(false)
106 , block_focus(0)
107 , m_userActionsMenu(new UserActionsMenu(this))
108 , m_sessionManager(new SessionManager(this))
109 , m_focusChain(std::make_unique<FocusChain>())
110 , m_applicationMenu(std::make_unique<ApplicationMenu>())
111 , m_placementTracker(std::make_unique<PlacementTracker>(this))
112 , m_outputConfigStore(std::make_unique<OutputConfigurationStore>())
113 , m_lidSwitchTracker(std::make_unique<LidSwitchTracker>())
114 , m_orientationSensor(std::make_unique<OrientationSensor>())
115{
116 _self = this;
117
118#if KWIN_BUILD_ACTIVITIES
119 if (kwinApp()->usesKActivities()) {
120 m_activities = std::make_unique<Activities>();
121 }
122 if (m_activities) {
123 connect(m_activities.get(), &Activities::currentChanged, this, &Workspace::updateCurrentActivity);
124 }
125#endif
126
127 delayFocusTimer = nullptr;
128
129 m_quickTileCombineTimer = new QTimer(this);
130 m_quickTileCombineTimer->setSingleShot(true);
131
132 m_rulebook = std::make_unique<RuleBook>();
133 m_rulebook->load();
134
135 m_screenEdges = std::make_unique<ScreenEdges>();
136
137 // VirtualDesktopManager needs to be created prior to init shortcuts
138 // and prior to TabBox, due to TabBox connecting to signals
139 // actual initialization happens in init()
140 VirtualDesktopManager::create(this);
141 // dbus interface
142 new VirtualDesktopManagerDBusInterface(VirtualDesktopManager::self());
143
144#if KWIN_BUILD_TABBOX
145 // need to create the tabbox before compositing scene is setup
146 m_tabbox = std::make_unique<TabBox::TabBox>();
147#endif
148
149 m_decorationBridge = std::make_unique<Decoration::DecorationBridge>();
150 m_decorationBridge->init();
151 connect(this, &Workspace::configChanged, m_decorationBridge.get(), &Decoration::DecorationBridge::reconfigure);
152
153 new DBusInterface(this);
154 m_outline = std::make_unique<Outline>();
155
156 initShortcuts();
157
158 init();
159}
160
161void Workspace::init()
162{
163 KSharedConfigPtr config = kwinApp()->config();
164 m_screenEdges->setConfig(config);
165 m_screenEdges->init();
166 connect(options, &Options::configChanged, m_screenEdges.get(), &ScreenEdges::reconfigure);
167 connect(VirtualDesktopManager::self(), &VirtualDesktopManager::layoutChanged, m_screenEdges.get(), &ScreenEdges::updateLayout);
168 connect(this, &Workspace::windowActivated, m_screenEdges.get(), &ScreenEdges::checkBlocking);
169
170 connect(this, &Workspace::windowRemoved, m_focusChain.get(), &FocusChain::remove);
171 connect(this, &Workspace::windowActivated, m_focusChain.get(), &FocusChain::setActiveWindow);
172 connect(VirtualDesktopManager::self(), &VirtualDesktopManager::currentChanged, m_focusChain.get(), [this]() {
173 m_focusChain->setCurrentDesktop(VirtualDesktopManager::self()->currentDesktop());
174 });
176 m_focusChain->setSeparateScreenFocus(options->isSeparateScreenFocus());
177
178 slotOutputBackendOutputsQueried();
179 connect(kwinApp()->outputBackend(), &OutputBackend::outputsQueried, this, &Workspace::slotOutputBackendOutputsQueried);
180
181 // create VirtualDesktopManager and perform dependency injection
182 VirtualDesktopManager *vds = VirtualDesktopManager::self();
183 connect(vds, &VirtualDesktopManager::desktopAdded, this, &Workspace::slotDesktopAdded);
184 connect(vds, &VirtualDesktopManager::desktopRemoved, this, &Workspace::slotDesktopRemoved);
185 connect(vds, &VirtualDesktopManager::currentChanged, this, &Workspace::slotCurrentDesktopChanged);
186 connect(vds, &VirtualDesktopManager::currentChanging, this, &Workspace::slotCurrentDesktopChanging);
187 connect(vds, &VirtualDesktopManager::currentChangingCancelled, this, &Workspace::slotCurrentDesktopChangingCancelled);
190 vds->setConfig(config);
191
192 // Now we know how many desktops we'll have, thus we initialize the positioning object
193 m_placement = std::make_unique<Placement>();
194
195 // positioning object needs to be created before the virtual desktops are loaded.
196 vds->load();
197 vds->updateLayout();
198 // makes sure any autogenerated id is saved, necessary as in case of xwayland, load will be called 2 times
199 // load is needed to be called again when starting xwayalnd to sync to RootInfo, see BUG 385260
200 vds->save();
201
202 vds->setCurrent(m_initialDesktop);
203
204 reconfigureTimer.setSingleShot(true);
205 updateToolWindowsTimer.setSingleShot(true);
206
207 connect(&reconfigureTimer, &QTimer::timeout, this, &Workspace::slotReconfigure);
208 connect(&updateToolWindowsTimer, &QTimer::timeout, this, &Workspace::slotUpdateToolWindows);
209
210 // TODO: do we really need to reconfigure everything when fonts change?
211 // maybe just reconfigure the decorations? Move this into libkdecoration?
212 QDBusConnection::sessionBus().connect(QString(),
213 QStringLiteral("/KDEPlatformTheme"),
214 QStringLiteral("org.kde.KDEPlatformTheme"),
215 QStringLiteral("refreshFonts"),
216 this, SLOT(reconfigure()));
217
218 m_activeWindow = nullptr;
219
220 // We want to have some xcb connection while tearing down X11 components. We don't really
221 // care if the xcb connection is broken or has an error.
222 connect(kwinApp(), &Application::x11ConnectionChanged, this, &Workspace::initializeX11);
223 connect(kwinApp(), &Application::x11ConnectionAboutToBeDestroyed, this, &Workspace::cleanupX11);
224 initializeX11();
225
226 Scripting::create(this);
227
228 if (auto server = waylandServer()) {
229 connect(server, &WaylandServer::windowAdded, this, &Workspace::addWaylandWindow);
230 connect(server, &WaylandServer::windowRemoved, this, &Workspace::removeWaylandWindow);
231 }
232
233 // broadcast that Workspace is ready, but first process all events.
234 QMetaObject::invokeMethod(this, &Workspace::workspaceInitialized, Qt::QueuedConnection);
235
236 // TODO: ungrabXServer()
237
238 connect(this, &Workspace::windowAdded, m_placementTracker.get(), &PlacementTracker::add);
239 connect(this, &Workspace::windowRemoved, m_placementTracker.get(), &PlacementTracker::remove);
240 m_placementTracker->init(getPlacementTrackerHash());
241
242 const auto applySensorChanges = [this]() {
243 m_orientationSensor->setEnabled(m_outputConfigStore->isAutoRotateActive(kwinApp()->outputBackend()->outputs(), kwinApp()->tabletModeManager()->effectiveTabletMode()));
244 const auto opt = m_outputConfigStore->queryConfig(kwinApp()->outputBackend()->outputs(), m_lidSwitchTracker->isLidClosed(), m_orientationSensor->reading(), kwinApp()->tabletModeManager()->effectiveTabletMode());
245 if (opt) {
246 const auto &[config, order, type] = *opt;
247 applyOutputConfiguration(config, order);
248 }
249 };
250 connect(m_lidSwitchTracker.get(), &LidSwitchTracker::lidStateChanged, this, applySensorChanges);
251 connect(m_orientationSensor.get(), &OrientationSensor::orientationChanged, this, applySensorChanges);
252 connect(kwinApp()->tabletModeManager(), &TabletModeManager::tabletModeChanged, this, applySensorChanges);
253 m_orientationSensor->setEnabled(m_outputConfigStore->isAutoRotateActive(kwinApp()->outputBackend()->outputs(), kwinApp()->tabletModeManager()->effectiveTabletMode()));
254}
255
256QString Workspace::getPlacementTrackerHash()
257{
258 QStringList hashes;
259 for (const auto &output : std::as_const(m_outputs)) {
260 QCryptographicHash hash(QCryptographicHash::Md5);
261 if (output->edid().isValid()) {
262 hash.addData(output->edid().raw());
263 } else {
264 hash.addData(output->name().toLatin1());
265 }
266 const auto geometry = output->geometry();
267 hash.addData(reinterpret_cast<const char *>(&geometry), sizeof(geometry));
268 hashes.push_back(QString::fromLatin1(hash.result().toHex()));
269 }
270 std::sort(hashes.begin(), hashes.end());
271 const auto hash = QCryptographicHash::hash(hashes.join(QString()).toLatin1(), QCryptographicHash::Md5);
272 return QString::fromLatin1(hash.toHex());
273}
274
275void Workspace::initializeX11()
276{
277 if (!kwinApp()->x11Connection()) {
278 return;
279 }
280
282
283 // first initialize the extensions
285
286 // Call this before XSelectInput() on the root window
287 m_startup = std::make_unique<KStartupInfo>(KStartupInfo::DisableKWinModule | KStartupInfo::AnnounceSilenceChanges, this);
288
289 // Select windowmanager privileges
290 selectWmInputEventMask();
291
292 if (kwinApp()->operationMode() == Application::OperationModeX11) {
293 m_wasUserInteractionFilter = std::make_unique<WasUserInteractionX11Filter>();
294 m_movingClientFilter = std::make_unique<MovingClientX11Filter>();
295 }
296 if (Xcb::Extensions::self()->isSyncAvailable()) {
297 m_syncAlarmFilter = std::make_unique<SyncAlarmX11Filter>();
298 }
299 kwinApp()->updateXTime(); // Needed for proper initialization of user_time in Client ctor
300
301 const uint32_t nullFocusValues[] = {true};
302 m_nullFocus = std::make_unique<Xcb::Window>(QRect(-1, -1, 1, 1), XCB_WINDOW_CLASS_INPUT_ONLY, XCB_CW_OVERRIDE_REDIRECT, nullFocusValues);
303 m_nullFocus->map();
304
305 RootInfo *rootInfo = RootInfo::create();
306 rootInfo->activate();
307
308 const auto vds = VirtualDesktopManager::self();
309 vds->setRootInfo(rootInfo);
310
311 // TODO: only in X11 mode
312 // Extra NETRootInfo instance in Client mode is needed to get the values of the properties
313 NETRootInfo client_info(kwinApp()->x11Connection(), NET::ActiveWindow | NET::CurrentDesktop);
314 bool sessionRestored = false;
315#ifndef QT_NO_SESSIONMANAGER
316 sessionRestored = qApp->isSessionRestored();
317#endif
318 if (!waylandServer()) {
319 if (!sessionRestored) {
320 m_initialDesktop = client_info.currentDesktop();
321 vds->setCurrent(m_initialDesktop);
322 }
323 }
324
325 // TODO: better value
326 rootInfo->setActiveWindow(XCB_WINDOW_NONE);
327 focusToNull();
328
329 if (!sessionRestored) {
330 ++block_focus; // Because it will be set below
331 }
332
333 {
334 // Begin updates blocker block
335 StackingUpdatesBlocker blocker(this);
336
337 Xcb::Tree tree(kwinApp()->x11RootWindow());
338 xcb_window_t *wins = xcb_query_tree_children(tree.data());
339
340 QList<Xcb::WindowAttributes> windowAttributes(tree->children_len);
341 QList<Xcb::WindowGeometry> windowGeometries(tree->children_len);
342
343 // Request the attributes and geometries of all toplevel windows
344 for (int i = 0; i < tree->children_len; i++) {
345 windowAttributes[i] = Xcb::WindowAttributes(wins[i]);
346 windowGeometries[i] = Xcb::WindowGeometry(wins[i]);
347 }
348
349 // Get the replies
350 for (int i = 0; i < tree->children_len; i++) {
351 Xcb::WindowAttributes attr(windowAttributes.at(i));
352
353 if (attr.isNull()) {
354 continue;
355 }
356
357 if (attr->override_redirect) {
358 if (attr->map_state == XCB_MAP_STATE_VIEWABLE && attr->_class != XCB_WINDOW_CLASS_INPUT_ONLY) {
359 // ### This will request the attributes again
360 createUnmanaged(wins[i]);
361 }
362 } else if (attr->map_state != XCB_MAP_STATE_UNMAPPED) {
363 if (Application::wasCrash()) {
364 fixPositionAfterCrash(wins[i], windowGeometries.at(i).data());
365 }
366
367 // ### This will request the attributes again
368 createX11Window(wins[i], true);
369 }
370 }
371
372 // Propagate windows, will really happen at the end of the updates blocker block
374
376
377 // NETWM spec says we have to set it to (0,0) if we don't support it
378 NETPoint *viewports = new NETPoint[VirtualDesktopManager::self()->count()];
379 rootInfo->setDesktopViewport(VirtualDesktopManager::self()->count(), *viewports);
380 delete[] viewports;
381
382 NETSize desktop_geometry;
383 desktop_geometry.width = m_geometry.width();
384 desktop_geometry.height = m_geometry.height();
385 rootInfo->setDesktopGeometry(desktop_geometry);
386
387 } // End updates blocker block
388
389 // TODO: only on X11?
390 Window *newActiveWindow = nullptr;
391 if (!sessionRestored) {
392 --block_focus;
393 newActiveWindow = findClient(Predicate::WindowMatch, client_info.activeWindow());
394 }
395 if (newActiveWindow == nullptr && activeWindow() == nullptr && should_get_focus.count() == 0) {
396 // No client activated in manage()
397 if (newActiveWindow == nullptr) {
398 newActiveWindow = topWindowOnDesktop(VirtualDesktopManager::self()->currentDesktop());
399 }
400 if (newActiveWindow == nullptr) {
401 newActiveWindow = findDesktop(true, VirtualDesktopManager::self()->currentDesktop());
402 }
403 }
404 if (newActiveWindow != nullptr) {
405 activateWindow(newActiveWindow);
406 }
407}
408
409void Workspace::cleanupX11()
410{
411 // We expect that other components will unregister their X11 event filters after the
412 // connection to the X server has been lost.
413
414 StackingUpdatesBlocker blocker(this);
415
416 // Use stacking_order, so that kwin --replace keeps stacking order.
417 const auto stack = stacking_order;
418 for (Window *window : stack) {
419 if (auto x11 = qobject_cast<X11Window *>(window); x11 && !x11->isDeleted()) {
420 x11->releaseWindow(true);
421 removeFromStack(window);
422 }
423 }
424
425 manual_overlays.clear();
426
427 VirtualDesktopManager *desktopManager = VirtualDesktopManager::self();
428 desktopManager->setRootInfo(nullptr);
429
433
434 m_movingClientFilter.reset();
435 m_startup.reset();
436 m_nullFocus.reset();
437 m_syncAlarmFilter.reset();
438 m_wasUserInteractionFilter.reset();
439}
440
442{
443 blockStackingUpdates(true);
444
445 cleanupX11();
446
447 if (waylandServer()) {
448 const QList<Window *> waylandWindows = waylandServer()->windows();
449 for (Window *window : waylandWindows) {
450 window->destroyWindow();
451 }
452 }
453
454 // We need a shadow copy because windows get removed as we go through them.
455 const QList<Window *> windows = m_windows;
456 for (Window *window : windows) {
457 window->destroyWindow();
458 }
459
460 m_rulebook.reset();
461 kwinApp()->config()->sync();
462
463 m_placement.reset();
464 delete m_windowKeysDialog;
465
466 if (m_placeholderOutput) {
467 m_placeholderOutput->unref();
468 }
469 m_tileManagers.clear();
470
471 for (Output *output : std::as_const(m_outputs)) {
472 output->unref();
473 }
474
475 _self = nullptr;
476}
477
478bool Workspace::applyOutputConfiguration(const OutputConfiguration &config, const QList<Output *> &outputOrder)
479{
480 if (!kwinApp()->outputBackend()->applyOutputChanges(config)) {
481 return false;
482 }
483 updateOutputs(outputOrder);
484 m_outputConfigStore->storeConfig(kwinApp()->outputBackend()->outputs(), m_lidSwitchTracker->isLidClosed(), config, outputOrder);
485 KConfig cfg(QStringLiteral("kdeglobals"));
486 KConfigGroup kscreenGroup = cfg.group(QStringLiteral("KScreen"));
487 const bool xwaylandClientsScale = kscreenGroup.readEntry("XwaylandClientsScale", true);
488 if (xwaylandClientsScale && !outputOrder.isEmpty()) {
489 double maxScale = 0;
490 for (Output *output : outputOrder) {
491 const auto changeset = config.constChangeSet(output);
492 maxScale = std::max(maxScale, changeset ? changeset->scale.value_or(output->scale()) : output->scale());
493 }
494 kwinApp()->setXwaylandScale(maxScale);
495 } else {
496 kwinApp()->setXwaylandScale(1);
497 }
498 m_orientationSensor->setEnabled(m_outputConfigStore->isAutoRotateActive(kwinApp()->outputBackend()->outputs(), kwinApp()->tabletModeManager()->effectiveTabletMode()));
499 return true;
500}
501
502void Workspace::updateOutputConfiguration()
503{
504 // There's conflict between this code and setVirtualOutputs(), need to adjust the tests.
505 if (QStandardPaths::isTestModeEnabled()) {
506 return;
507 }
508
509 const auto outputs = kwinApp()->outputBackend()->outputs();
510 if (outputs.empty()) {
511 // nothing to do
512 setOutputOrder({});
513 return;
514 }
515
516 // Update the output order to a fallback list, to avoid dangling pointers
517 const auto setFallbackOutputOrder = [this, &outputs]() {
518 auto newOrder = outputs;
519 newOrder.erase(std::remove_if(newOrder.begin(), newOrder.end(), [](Output *o) {
520 return !o->isEnabled();
521 }),
522 newOrder.end());
523 std::sort(newOrder.begin(), newOrder.end(), [](Output *left, Output *right) {
524 return left->name() < right->name();
525 });
526 setOutputOrder(newOrder);
527 };
528
529 const auto opt = m_outputConfigStore->queryConfig(outputs, m_lidSwitchTracker->isLidClosed(), m_orientationSensor->reading(), kwinApp()->tabletModeManager()->effectiveTabletMode());
530 if (!opt) {
531 return;
532 }
533 const auto &[cfg, order, type] = *opt;
534 if (!applyOutputConfiguration(cfg, order)) {
535 qCWarning(KWIN_CORE) << "Applying output config failed!";
536 setFallbackOutputOrder();
537 return;
538 }
539 setOutputOrder(order);
541 const bool hasInternal = std::any_of(outputs.begin(), outputs.end(), [](Output *o) {
542 return o->isInternal();
543 });
544 if (hasInternal && outputs.size() == 2) {
545 // show the OSD with output configuration presets
546 QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kscreen.osdService"),
547 QStringLiteral("/org/kde/kscreen/osdService"),
548 QStringLiteral("org.kde.kscreen.osdService"),
549 QStringLiteral("showActionSelector"));
550 QDBusConnection::sessionBus().asyncCall(message);
551 }
552 }
553}
554
555void Workspace::setupWindowConnections(Window *window)
556{
557 connect(window, &Window::minimizedChanged, this, std::bind(&Workspace::windowMinimizedChanged, this, window));
558 connect(window, &Window::fullScreenChanged, m_screenEdges.get(), &ScreenEdges::checkBlocking);
559}
560
562{
563 if (below == above) {
564 return;
565 }
566
567 QList<Constraint *> parents;
568 QList<Constraint *> children;
569 for (Constraint *constraint : std::as_const(m_constraints)) {
570 if (constraint->below == below && constraint->above == above) {
571 return;
572 }
573 if (constraint->below == above) {
574 children << constraint;
575 } else if (constraint->above == below) {
576 parents << constraint;
577 }
578 }
579
580 Constraint *constraint = new Constraint();
581 constraint->parents = parents;
582 constraint->below = below;
583 constraint->above = above;
584 constraint->children = children;
585 m_constraints << constraint;
586
587 for (Constraint *parent : std::as_const(parents)) {
588 parent->children << constraint;
589 }
590
591 for (Constraint *child : std::as_const(children)) {
592 child->parents << constraint;
593 }
594
596}
597
599{
600 Constraint *constraint = nullptr;
601 for (int i = 0; i < m_constraints.count(); ++i) {
602 if (m_constraints[i]->below == below && m_constraints[i]->above == above) {
603 constraint = m_constraints.takeAt(i);
604 break;
605 }
606 }
607
608 if (!constraint) {
609 return;
610 }
611
612 const QList<Constraint *> parents = constraint->parents;
613 for (Constraint *parent : parents) {
614 parent->children.removeOne(constraint);
615 }
616
617 const QList<Constraint *> children = constraint->children;
618 for (Constraint *child : children) {
619 child->parents.removeOne(constraint);
620 }
621
622 delete constraint;
624}
625
626void Workspace::addToStack(Window *window)
627{
628 // If the stacking order of a window has been restored from the session, that
629 // window will already be in the stack when Workspace::addX11Window() is called.
630 if (!unconstrained_stacking_order.contains(window)) {
631 unconstrained_stacking_order.append(window);
632 }
633 if (!stacking_order.contains(window)) {
634 stacking_order.append(window);
635 }
636}
637
638void Workspace::removeFromStack(Window *window)
639{
640 unconstrained_stacking_order.removeAll(window);
641 stacking_order.removeAll(window);
642
643 for (int i = m_constraints.count() - 1; i >= 0; --i) {
644 Constraint *constraint = m_constraints[i];
645 const bool isBelow = (constraint->below == window);
646 const bool isAbove = (constraint->above == window);
647 if (!isBelow && !isAbove) {
648 continue;
649 }
650 if (isBelow) {
651 for (Constraint *child : std::as_const(constraint->children)) {
652 child->parents.removeOne(constraint);
653 }
654 } else {
655 for (Constraint *parent : std::as_const(constraint->parents)) {
656 parent->children.removeOne(constraint);
657 }
658 }
659 delete m_constraints.takeAt(i);
660 }
661}
662
663X11Window *Workspace::createX11Window(xcb_window_t windowId, bool is_mapped)
664{
665 StackingUpdatesBlocker blocker(this);
666 X11Window *window = new X11Window();
667 setupWindowConnections(window);
668 if (!window->manage(windowId, is_mapped)) {
670 return nullptr;
671 }
672 addX11Window(window);
673 Q_EMIT windowAdded(window);
674 return window;
675}
676
677X11Window *Workspace::createUnmanaged(xcb_window_t windowId)
678{
679 if (kwinApp()->x11CompositeWindow() == windowId) {
680 return nullptr;
681 }
682 X11Window *window = new X11Window();
683 if (!window->track(windowId)) {
685 return nullptr;
686 }
687 addUnmanaged(window);
688 return window;
689}
690
691void Workspace::addX11Window(X11Window *window)
692{
693 if (showingDesktop() && breaksShowingDesktop(window)) {
694 setShowingDesktop(false);
695 }
696
697 Group *grp = findGroup(window->window());
698 if (grp != nullptr) {
699 grp->gotLeader(window);
700 }
701
702 if (window->isDesktop()) {
703 if (m_activeWindow == nullptr && should_get_focus.isEmpty() && window->isOnCurrentDesktop()) {
704 requestFocus(window); // TODO: Make sure desktop is active after startup if there's no other window active
705 }
706 } else {
707 m_focusChain->update(window, FocusChain::Update);
708 }
709 Q_ASSERT(!m_windows.contains(window));
710 m_windows.append(window);
711 addToStack(window);
712 if (window->hasStrut()) {
713 updateClientArea(); // This cannot be in manage(), because the window got added only now
714 }
715 window->updateLayer();
716 if (window->isDesktop()) {
717 raiseWindow(window);
718 // If there's no active window, make this desktop the active one
719 if (activeWindow() == nullptr && should_get_focus.count() == 0) {
720 activateWindow(findDesktop(true, VirtualDesktopManager::self()->currentDesktop()));
721 }
722 }
723 window->checkActiveModal();
724 checkTransients(window->window()); // SELI TODO: Does this really belong here?
725 updateStackingOrder(true); // Propagate new window
726 if (window->isUtility() || window->isMenu() || window->isToolbar()) {
727 updateToolWindows(true);
728 }
729 updateTabbox();
730}
731
732void Workspace::addUnmanaged(X11Window *window)
733{
734 Q_ASSERT(!m_windows.contains(window));
735 m_windows.append(window);
736 addToStack(window);
738 Q_EMIT windowAdded(window);
739}
740
745{
746 Q_ASSERT(m_windows.contains(window));
747 Group *group = findGroup(window->window());
748 if (group != nullptr) {
749 group->lostLeader();
750 }
751 removeWindow(window);
752}
753
755{
756 Q_ASSERT(m_windows.contains(window));
757 m_windows.removeOne(window);
758 removeFromStack(window);
760 Q_EMIT windowRemoved(window);
761}
762
764{
765 Q_ASSERT(!deleted.contains(c));
766 deleted.append(c);
767}
768
770{
771 Q_ASSERT(deleted.contains(c));
772 Q_EMIT deletedRemoved(c);
773 deleted.removeAll(c);
774 removeFromStack(c);
775}
776
777void Workspace::addWaylandWindow(Window *window)
778{
779 if (showingDesktop() && breaksShowingDesktop(window)) {
780 setShowingDesktop(false);
781 }
782
783 setupWindowConnections(window);
784 window->updateLayer();
785
786 if (window->isPlaceable()) {
787 const QRectF area = clientArea(PlacementArea, window, activeOutput());
788 bool placementDone = false;
789 if (window->isRequestedFullScreen()) {
790 placementDone = true;
791 }
793 placementDone = true;
794 }
795 if (window->rules()->checkPosition(invalidPoint, true) != invalidPoint) {
796 placementDone = true;
797 }
798 if (!placementDone) {
799 m_placement->place(window, area);
800 }
801 }
802 Q_ASSERT(!m_windows.contains(window));
803 m_windows.append(window);
804 addToStack(window);
805
807 if (window->hasStrut()) {
809 }
810 if (window->wantsInput() && !window->isMinimized()) {
811 // Never activate a window on its own in "Extreme" mode.
813 activateWindow(window);
814 }
815 }
816 updateTabbox();
817 Q_EMIT windowAdded(window);
818}
819
820void Workspace::removeWaylandWindow(Window *window)
821{
822 windowHidden(window);
823 removeWindow(window);
824}
825
826void Workspace::removeWindow(Window *window)
827{
828 if (window == m_activePopupWindow) {
829 closeActivePopup();
830 }
831 if (m_userActionsMenu->isMenuWindow(window)) {
832 m_userActionsMenu->close();
833 }
834
835 m_windows.removeAll(window);
836 if (window == m_delayFocusWindow) {
838 }
839 attention_chain.removeAll(window);
840 should_get_focus.removeAll(window);
841 if (window == m_activeWindow) {
842 m_activeWindow = nullptr;
843 }
844 if (window == m_lastActiveWindow) {
845 m_lastActiveWindow = nullptr;
846 }
847 if (m_windowKeysWindow == window) {
849 }
850 if (window->hasStrut()) {
852 }
853
854 Q_EMIT windowRemoved(window);
855
857 updateTabbox();
858}
859
860void Workspace::updateToolWindows(bool also_hide)
861{
862 // TODO: What if Client's transiency/group changes? should this be called too? (I'm paranoid, am I not?)
864 for (auto it = m_windows.constBegin(); it != m_windows.constEnd(); ++it) {
865 if (X11Window *x11Window = qobject_cast<X11Window *>(*it)) {
866 x11Window->setHidden(false);
867 }
868 }
869 return;
870 }
871 const Group *group = nullptr;
872 auto window = m_activeWindow;
873 // Go up in transiency hiearchy, if the top is found, only tool transients for the top mainwindow
874 // will be shown; if a group transient is group, all tools in the group will be shown
875 while (window != nullptr) {
876 if (!window->isTransient()) {
877 break;
878 }
879 if (window->groupTransient()) {
880 group = window->group();
881 break;
882 }
883 window = window->transientFor();
884 }
885 // Use stacking order only to reduce flicker, it doesn't matter if block_stacking_updates == 0,
886 // I.e. if it's not up to date
887
888 // SELI TODO: But maybe it should - what if a new window has been added that's not in stacking order yet?
889 QList<Window *> to_show, to_hide;
890 for (auto it = stacking_order.constBegin(); it != stacking_order.constEnd(); ++it) {
891 auto c = *it;
892 if (!c->isClient()) {
893 continue;
894 }
895 if (c->isUtility() || c->isMenu() || c->isToolbar()) {
896 bool show = true;
897 if (!c->isTransient()) {
898 if (!c->group() || c->group()->members().count() == 1) { // Has its own group, keep always visible
899 show = true;
900 } else if (window != nullptr && c->group() == window->group()) {
901 show = true;
902 } else {
903 show = false;
904 }
905 } else {
906 if (group != nullptr && c->group() == group) {
907 show = true;
908 } else if (window != nullptr && window->hasTransient(c, true)) {
909 show = true;
910 } else {
911 show = false;
912 }
913 }
914 if (!show && also_hide) {
915 const auto mainwindows = c->mainWindows();
916 // Don't hide utility windows which are standalone(?) or
917 // have e.g. kicker as mainwindow
918 if (mainwindows.isEmpty()) {
919 show = true;
920 }
921 for (auto it2 = mainwindows.constBegin(); it2 != mainwindows.constEnd(); ++it2) {
922 if ((*it2)->isSpecialWindow()) {
923 show = true;
924 }
925 }
926 if (!show) {
927 to_hide.append(c);
928 }
929 }
930 if (show) {
931 to_show.append(c);
932 }
933 }
934 } // First show new ones, then hide
935 for (int i = to_show.size() - 1; i >= 0; --i) { // From topmost
936 // TODO: Since this is in stacking order, the order of taskbar entries changes :(
937 to_show.at(i)->setHidden(false);
938 }
939 if (also_hide) {
940 for (auto it = to_hide.constBegin(); it != to_hide.constEnd(); ++it) { // From bottommost
941 (*it)->setHidden(true);
942 }
943 updateToolWindowsTimer.stop();
944 } else { // setActiveWindow() is after called with NULL window, quickly followed
945 // by setting a new window, which would result in flickering
947 }
948}
949
951{
952 updateToolWindowsTimer.start(200);
953}
954
955void Workspace::slotUpdateToolWindows()
956{
957 updateToolWindows(true);
958}
959
960void Workspace::slotReloadConfig()
961{
962 reconfigure();
963}
964
966{
967 reconfigureTimer.start(200);
968}
969
975{
976 qCDebug(KWIN_CORE) << "Workspace::slotReconfigure()";
977 reconfigureTimer.stop();
978
979 bool borderlessMaximizedWindows = options->borderlessMaximizedWindows();
980
981 kwinApp()->config()->reparseConfiguration();
983
984 Q_EMIT configChanged();
985 m_userActionsMenu->discard();
986 updateToolWindows(true);
987
988 m_rulebook->load();
989 for (Window *window : std::as_const(m_windows)) {
990 if (window->supportsWindowRules()) {
991 window->evaluateWindowRules();
992 m_rulebook->discardUsed(window, false);
993 }
994 }
995
996 if (borderlessMaximizedWindows != options->borderlessMaximizedWindows() && !options->borderlessMaximizedWindows()) {
997 // in case borderless maximized windows option changed and new option
998 // is to have borders, we need to unset the borders for all maximized windows
999 for (auto it = m_windows.cbegin(); it != m_windows.cend(); ++it) {
1000 if ((*it)->maximizeMode() == MaximizeFull) {
1001 (*it)->checkNoBorder();
1002 }
1003 }
1004 }
1005}
1006
1007void Workspace::slotCurrentDesktopChanged(VirtualDesktop *oldDesktop, VirtualDesktop *newDesktop)
1008{
1009 closeActivePopup();
1010 ++block_focus;
1011 StackingUpdatesBlocker blocker(this);
1012 updateWindowVisibilityOnDesktopChange(newDesktop);
1013 // Restore the focus on this desktop
1014 --block_focus;
1015
1016 activateWindowOnNewDesktop(newDesktop);
1017 Q_EMIT currentDesktopChanged(oldDesktop, m_moveResizeWindow);
1018}
1019
1020void Workspace::slotCurrentDesktopChanging(VirtualDesktop *currentDesktop, QPointF offset)
1021{
1022 closeActivePopup();
1023 Q_EMIT currentDesktopChanging(currentDesktop, offset, m_moveResizeWindow);
1024}
1025
1026void Workspace::slotCurrentDesktopChangingCancelled()
1027{
1029}
1030
1031void Workspace::updateWindowVisibilityOnDesktopChange(VirtualDesktop *newDesktop)
1032{
1033 for (auto it = stacking_order.constBegin(); it != stacking_order.constEnd(); ++it) {
1034 X11Window *c = qobject_cast<X11Window *>(*it);
1035 if (!c) {
1036 continue;
1037 }
1038 if (!c->isOnDesktop(newDesktop) && c != m_moveResizeWindow && c->isOnCurrentActivity()) {
1039 (c)->updateVisibility();
1040 }
1041 }
1042 // Now propagate the change, after hiding, before showing
1043 if (rootInfo()) {
1044 rootInfo()->setCurrentDesktop(VirtualDesktopManager::self()->current());
1045 }
1046
1047 if (m_moveResizeWindow && !m_moveResizeWindow->isOnDesktop(newDesktop)) {
1048 m_moveResizeWindow->setDesktops({newDesktop});
1049 }
1050
1051 for (int i = stacking_order.size() - 1; i >= 0; --i) {
1052 X11Window *c = qobject_cast<X11Window *>(stacking_order.at(i));
1053 if (!c) {
1054 continue;
1055 }
1056 if (c->isOnDesktop(newDesktop) && c->isOnCurrentActivity()) {
1057 c->updateVisibility();
1058 }
1059 }
1060 if (showingDesktop()) { // Do this only after desktop change to avoid flicker
1061 setShowingDesktop(false);
1062 }
1063}
1064
1065void Workspace::activateWindowOnNewDesktop(VirtualDesktop *desktop)
1066{
1067 Window *window = nullptr;
1069 window = findWindowToActivateOnDesktop(desktop);
1070 }
1071 // If "unreasonable focus policy" and m_activeWindow is on_all_desktops and
1072 // under mouse (Hence == old_active_window), conserve focus.
1073 // (Thanks to Volker Schatz <V.Schatz at thphys.uni-heidelberg.de>)
1074 else if (m_activeWindow && m_activeWindow->isShown() && m_activeWindow->isOnCurrentDesktop()) {
1075 window = m_activeWindow;
1076 }
1077
1078 if (!window) {
1079 window = findDesktop(true, desktop);
1080 }
1081
1082 if (window != m_activeWindow) {
1083 setActiveWindow(nullptr);
1084 }
1085
1086 if (window) {
1087 requestFocus(window);
1088 } else {
1089 focusToNull();
1090 }
1091}
1092
1093Window *Workspace::findWindowToActivateOnDesktop(VirtualDesktop *desktop)
1094{
1095 if (m_moveResizeWindow != nullptr && m_activeWindow == m_moveResizeWindow && m_focusChain->contains(m_activeWindow, desktop) && m_activeWindow->isShown() && m_activeWindow->isOnCurrentDesktop()) {
1096 // A requestFocus call will fail, as the window is already active
1097 return m_activeWindow;
1098 }
1099 // from actiavtion.cpp
1101 auto it = stackingOrder().constEnd();
1102 while (it != stackingOrder().constBegin()) {
1103 auto window = *(--it);
1104 if (!window->isClient()) {
1105 continue;
1106 }
1107
1108 if (!(!window->isShade() && window->isShown() && window->isOnDesktop(desktop) && window->isOnCurrentActivity() && window->isOnActiveOutput())) {
1109 continue;
1110 }
1111
1112 if (window->hitTest(Cursors::self()->mouse()->pos())) {
1113 if (!window->isDesktop()) {
1114 return window;
1115 }
1116 break; // unconditional break - we do not pass the focus to some window below an unusable one
1117 }
1118 }
1119 }
1120 return m_focusChain->getForActivation(desktop);
1121}
1122
1130void Workspace::updateCurrentActivity(const QString &new_activity)
1131{
1132#if KWIN_BUILD_ACTIVITIES
1133 if (!m_activities) {
1134 return;
1135 }
1136 // closeActivePopup();
1137 ++block_focus;
1138 // TODO: Q_ASSERT( block_stacking_updates == 0 ); // Make sure stacking_order is up to date
1139 StackingUpdatesBlocker blocker(this);
1140
1141 // Optimized Desktop switching: unmapping done from back to front
1142 // mapping done from front to back => less exposure events
1143 // Notify::raise((Notify::Event) (Notify::DesktopChange+new_desktop));
1144
1145 for (auto it = stacking_order.constBegin(); it != stacking_order.constEnd(); ++it) {
1146 X11Window *window = qobject_cast<X11Window *>(*it);
1147 if (!window) {
1148 continue;
1149 }
1150 if (!window->isOnActivity(new_activity) && window != m_moveResizeWindow && window->isOnCurrentDesktop()) {
1151 window->updateVisibility();
1152 }
1153 }
1154
1155 // Now propagate the change, after hiding, before showing
1156 // rootInfo->setCurrentDesktop( currentDesktop() );
1157
1158 /* TODO someday enable dragging windows to other activities
1159 if ( m_moveResizeWindow && !m_moveResizeWindow->isOnDesktop( new_desktop ))
1160 {
1161 m_moveResizeWindow->setDesktop( new_desktop );
1162 */
1163
1164 for (int i = stacking_order.size() - 1; i >= 0; --i) {
1165 X11Window *window = qobject_cast<X11Window *>(stacking_order.at(i));
1166 if (!window) {
1167 continue;
1168 }
1169 if (window->isOnActivity(new_activity)) {
1170 window->updateVisibility();
1171 }
1172 }
1173
1174 // FIXME not sure if I should do this either
1175 if (showingDesktop()) { // Do this only after desktop change to avoid flicker
1176 setShowingDesktop(false);
1177 }
1178
1179 // Restore the focus on this desktop
1180 --block_focus;
1181 Window *window = nullptr;
1182
1183 // FIXME below here is a lot of focuschain stuff, probably all wrong now
1184 // Keep active window focused if it's on the new activity
1185 if (m_activeWindow && m_activeWindow->isShown() && m_activeWindow->isOnCurrentDesktop() && m_activeWindow->isOnCurrentActivity()) {
1186 window = m_activeWindow;
1187 } else if (options->focusPolicyIsReasonable()) {
1188 // Search in focus chain
1189 window = m_focusChain->getForActivation(VirtualDesktopManager::self()->currentDesktop());
1190 }
1191
1192 if (!window) {
1193 window = findDesktop(true, VirtualDesktopManager::self()->currentDesktop());
1194 }
1195
1196 if (window != m_activeWindow) {
1197 setActiveWindow(nullptr);
1198 }
1199
1200 if (window) {
1201 requestFocus(window);
1202 } else {
1203 focusToNull();
1204 }
1205
1206 Q_EMIT currentActivityChanged();
1207#endif
1208}
1209
1210Output *Workspace::outputAt(const QPointF &pos) const
1211{
1212 Output *bestOutput = nullptr;
1213 qreal minDistance;
1214
1215 for (Output *output : std::as_const(m_outputs)) {
1216 const QRectF geo = output->geometry();
1217
1218 const QPointF closestPoint(std::clamp(pos.x(), geo.x(), geo.x() + geo.width() - 1),
1219 std::clamp(pos.y(), geo.y(), geo.y() + geo.height() - 1));
1220
1221 const QPointF ray = closestPoint - pos;
1222 const qreal distance = ray.x() * ray.x() + ray.y() * ray.y();
1223 if (!bestOutput || distance < minDistance) {
1224 minDistance = distance;
1225 bestOutput = output;
1226 }
1227 }
1228 return bestOutput;
1229}
1230
1231Output *Workspace::findOutput(Output *reference, Direction direction, bool wrapAround) const
1232{
1233 QList<Output *> relevantOutputs;
1234 std::copy_if(m_outputs.begin(), m_outputs.end(), std::back_inserter(relevantOutputs), [reference, direction](Output *output) {
1235 switch (direction) {
1236 case DirectionEast:
1237 case DirectionWest:
1238 // filter for outputs on same horizontal line
1239 return output->geometry().top() <= reference->geometry().bottom() && output->geometry().bottom() >= reference->geometry().top();
1240 case DirectionSouth:
1241 case DirectionNorth:
1242 // filter for outputs on same vertical line
1243 return output->geometry().left() <= reference->geometry().right() && output->geometry().right() >= reference->geometry().left();
1244 default:
1245 // take all outputs
1246 return true;
1247 }
1248 });
1249
1250 std::sort(relevantOutputs.begin(), relevantOutputs.end(), [direction](const Output *o1, const Output *o2) {
1251 switch (direction) {
1252 case DirectionEast:
1253 case DirectionWest:
1254 // order outputs from left to right
1255 return o1->geometry().center().x() < o2->geometry().center().x();
1256 case DirectionSouth:
1257 case DirectionNorth:
1258 // order outputs from top to bottom
1259 return o1->geometry().center().y() < o2->geometry().center().y();
1260 default:
1261 // order outputs from top to bottom, then left to right
1262 // case 1: o1 is above o2
1263 // case 2: o1 is not below o2, and o1 is left of o2
1264 return o1->geometry().y() + o1->geometry().height() <= o2->geometry().top() || (o1->geometry().top() < o2->geometry().y() + o2->geometry().height() && o1->geometry().left() < o2->geometry().left());
1265 }
1266 });
1267
1268 const int index = relevantOutputs.indexOf(reference);
1269 Q_ASSERT(index != -1);
1270 switch (direction) {
1271 case DirectionEast:
1272 case DirectionSouth:
1273 case DirectionNext:
1274 // go forward in the list
1275 return relevantOutputs[wrapAround ? (index + 1) % relevantOutputs.count() : std::min(index + 1, (int)relevantOutputs.count() - 1)];
1276 case DirectionWest:
1277 case DirectionNorth:
1278 case DirectionPrev:
1279 // go backward in the list
1280 return relevantOutputs[wrapAround ? (index + relevantOutputs.count() - 1) % relevantOutputs.count() : std::max(index - 1, 0)];
1281 default:
1282 Q_UNREACHABLE();
1283 }
1284}
1285
1286void Workspace::slotOutputBackendOutputsQueried()
1287{
1288 if (waylandServer()) {
1289 updateOutputConfiguration();
1290 }
1291 updateOutputs();
1292}
1293
1294void Workspace::updateOutputs(const QList<Output *> &outputOrder)
1295{
1296 const auto availableOutputs = kwinApp()->outputBackend()->outputs();
1297 const auto oldOutputs = m_outputs;
1298
1299 m_outputs.clear();
1300 for (Output *output : availableOutputs) {
1301 if (!output->isNonDesktop() && output->isEnabled()) {
1302 m_outputs.append(output);
1303 }
1304 }
1305
1306 // The workspace requires at least one output connected.
1307 if (m_outputs.isEmpty()) {
1308 if (!m_placeholderOutput) {
1309 m_placeholderOutput = new PlaceholderOutput(QSize(1920, 1080), 1);
1310 m_placeholderFilter = std::make_unique<PlaceholderInputEventFilter>();
1311 input()->prependInputEventFilter(m_placeholderFilter.get());
1312 }
1313 m_outputs.append(m_placeholderOutput);
1314 } else {
1315 if (m_placeholderOutput) {
1316 m_placeholderOutput->unref();
1317 m_placeholderOutput = nullptr;
1318 m_placeholderFilter.reset();
1319 }
1320 }
1321
1322 if (!m_activeOutput || !m_outputs.contains(m_activeOutput)) {
1323 setActiveOutput(m_outputs[0]);
1324 }
1325 if (!m_outputs.contains(m_activeCursorOutput)) {
1326 m_activeCursorOutput = nullptr;
1327 }
1328
1329 if (!outputOrder.empty()) {
1330 setOutputOrder(outputOrder);
1331 } else {
1332 // ensure all enabled but no disabled outputs are in the output order
1333 for (Output *output : std::as_const(m_outputs)) {
1334 if (output->isEnabled() && !m_outputOrder.contains(output)) {
1335 m_outputOrder.push_back(output);
1336 }
1337 }
1338 m_outputOrder.erase(std::remove_if(m_outputOrder.begin(), m_outputOrder.end(), [this](Output *output) {
1339 return !m_outputs.contains(output);
1340 }),
1341 m_outputOrder.end());
1342 }
1343
1344 const QSet<Output *> oldOutputsSet(oldOutputs.constBegin(), oldOutputs.constEnd());
1345 const QSet<Output *> outputsSet(m_outputs.constBegin(), m_outputs.constEnd());
1346
1347 const auto added = outputsSet - oldOutputsSet;
1348 for (Output *output : added) {
1349 output->ref();
1350 m_tileManagers[output] = std::make_unique<TileManager>(output);
1351 connect(output, &Output::aboutToTurnOff, this, &Workspace::createDpmsFilter);
1352 connect(output, &Output::wakeUp, this, &Workspace::maybeDestroyDpmsFilter);
1353 if (output->dpmsMode() != Output::DpmsMode::On) {
1354 createDpmsFilter();
1355 }
1356 Q_EMIT outputAdded(output);
1357 }
1358 maybeDestroyDpmsFilter();
1359
1360 const auto removed = oldOutputsSet - outputsSet;
1361 for (Output *output : removed) {
1362 Q_EMIT outputRemoved(output);
1363 auto tileManager = std::move(m_tileManagers[output]);
1364 m_tileManagers.erase(output);
1365
1366 // Evacuate windows from the defunct custom tile tree.
1367 tileManager->rootTile()->visitDescendants([](const Tile *child) {
1368 const QList<Window *> windows = child->windows();
1369 for (Window *window : windows) {
1370 window->setTile(nullptr);
1371 }
1372 });
1373
1374 // Migrate windows from the defunct quick tile to a quick tile tree on another output.
1375 static constexpr QuickTileMode quickTileModes[] = {
1376 QuickTileFlag::Left,
1377 QuickTileFlag::Right,
1378 QuickTileFlag::Top,
1379 QuickTileFlag::Bottom,
1380 QuickTileFlag::Top | QuickTileFlag::Left,
1381 QuickTileFlag::Top | QuickTileFlag::Right,
1382 QuickTileFlag::Bottom | QuickTileFlag::Left,
1383 QuickTileFlag::Bottom | QuickTileFlag::Right,
1384 };
1385
1386 for (const QuickTileMode &quickTileMode : quickTileModes) {
1387 Tile *quickTile = tileManager->quickTile(quickTileMode);
1388 const QList<Window *> windows = quickTile->windows();
1389 if (windows.isEmpty()) {
1390 continue;
1391 }
1392
1393 Output *bestOutput = outputAt(output->geometry().center());
1394 Tile *bestTile = m_tileManagers[bestOutput]->quickTile(quickTileMode);
1395
1396 for (Window *window : windows) {
1397 window->setTile(bestTile);
1398 }
1399 }
1400 }
1401
1402 desktopResized();
1403
1404 for (Output *output : removed) {
1405 output->unref();
1406 }
1407
1408 Q_EMIT outputsChanged();
1409}
1410
1411void Workspace::createDpmsFilter()
1412{
1413 if (!m_dpmsFilter) {
1414 m_dpmsFilter = std::make_unique<DpmsInputEventFilter>();
1415 input()->prependInputEventFilter(m_dpmsFilter.get());
1416 }
1417}
1418
1419void Workspace::maybeDestroyDpmsFilter()
1420{
1421 const bool allOn = std::all_of(m_outputs.begin(), m_outputs.end(), [](Output *output) {
1422 return output->dpmsMode() == Output::DpmsMode::On && !output->isPlaceholder();
1423 });
1424 if (allOn) {
1425 m_dpmsFilter.reset();
1426 }
1427}
1428
1429void Workspace::slotDesktopAdded(VirtualDesktop *desktop)
1430{
1431 m_focusChain->addDesktop(desktop);
1432 m_placement->reinitCascading();
1433 updateClientArea();
1434}
1435
1436void Workspace::slotDesktopRemoved(VirtualDesktop *desktop)
1437{
1438 for (auto it = m_windows.constBegin(); it != m_windows.constEnd(); ++it) {
1439 if (!(*it)->desktops().contains(desktop)) {
1440 continue;
1441 }
1442 if ((*it)->desktops().count() > 1) {
1443 (*it)->leaveDesktop(desktop);
1444 } else {
1445 const uint desktopId = std::min(desktop->x11DesktopNumber(), VirtualDesktopManager::self()->count());
1446 sendWindowToDesktops(*it, {VirtualDesktopManager::self()->desktopForX11Id(desktopId)}, true);
1447 }
1448 }
1449
1450 for (auto it = deleted.constBegin(); it != deleted.constEnd(); ++it) {
1451 if ((*it)->desktops().contains(desktop)) {
1452 (*it)->leaveDesktop(desktop);
1453 }
1454 }
1455
1456 updateClientArea();
1457 m_placement->reinitCascading();
1458 m_focusChain->removeDesktop(desktop);
1459}
1460
1461void Workspace::selectWmInputEventMask()
1462{
1463 uint32_t presentMask = 0;
1464 Xcb::WindowAttributes attr(kwinApp()->x11RootWindow());
1465 if (!attr.isNull()) {
1466 presentMask = attr->your_event_mask;
1467 }
1468
1469 const uint32_t wmMask = XCB_EVENT_MASK_KEY_PRESS
1470 | XCB_EVENT_MASK_PROPERTY_CHANGE
1471 | XCB_EVENT_MASK_COLOR_MAP_CHANGE
1472 | XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT
1473 | XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY
1474 | XCB_EVENT_MASK_FOCUS_CHANGE // For NotifyDetailNone
1475 | XCB_EVENT_MASK_EXPOSURE;
1476
1477 Xcb::selectInput(kwinApp()->x11RootWindow(), presentMask | wmMask);
1478}
1479
1485void Workspace::sendWindowToDesktops(Window *window, const QList<VirtualDesktop *> &desktops, bool dont_activate)
1486{
1487 const QList<VirtualDesktop *> oldDesktops = window->desktops();
1488 const bool wasOnCurrent = window->isOnCurrentDesktop();
1489 window->setDesktops(desktops);
1490 if (window->desktops() != desktops) { // No change or desktop forced
1491 return;
1492 }
1493
1494 if (window->isOnCurrentDesktop()) {
1495 if (window->wantsTabFocus() && options->focusPolicyIsReasonable() && !wasOnCurrent && // for stickyness changes
1496 !dont_activate) {
1497 requestFocus(window);
1498 } else {
1499 restackWindowUnderActive(window);
1500 }
1501 } else {
1502 raiseWindow(window);
1503 }
1504
1505 window->checkWorkspacePosition(QRect(), oldDesktops.isEmpty() ? nullptr : oldDesktops.last());
1506
1507 auto transients_stacking_order = ensureStackingOrder(window->transients());
1508 for (auto it = transients_stacking_order.constBegin(); it != transients_stacking_order.constEnd(); ++it) {
1509 sendWindowToDesktops(*it, window->desktops(), dont_activate);
1510 }
1511 updateClientArea();
1512}
1513
1514void Workspace::sendWindowToOutput(Window *window, Output *output)
1515{
1516 window->sendToOutput(output);
1517}
1518
1522void Workspace::delayFocus()
1523{
1524 requestFocus(m_delayFocusWindow);
1525 cancelDelayFocus();
1526}
1527
1528void Workspace::requestDelayFocus(Window *window)
1529{
1530 m_delayFocusWindow = window;
1531 delete delayFocusTimer;
1532 delayFocusTimer = new QTimer(this);
1533 connect(delayFocusTimer, &QTimer::timeout, this, &Workspace::delayFocus);
1534 delayFocusTimer->setSingleShot(true);
1535 delayFocusTimer->start(options->delayFocusInterval());
1536}
1537
1538void Workspace::cancelDelayFocus()
1539{
1540 delete delayFocusTimer;
1541 delayFocusTimer = nullptr;
1542 m_delayFocusWindow = nullptr;
1543}
1544
1545bool Workspace::checkStartupNotification(xcb_window_t w, KStartupInfoId &id, KStartupInfoData &data)
1546{
1547 return m_startup->checkStartup(w, id, data) == KStartupInfo::Match;
1548}
1549
1554void Workspace::focusToNull()
1555{
1556 if (m_nullFocus) {
1557 should_get_focus.clear();
1558 m_nullFocus->focus();
1559 }
1560}
1561
1562bool Workspace::breaksShowingDesktop(Window *window) const
1563{
1564 return !(window->isUnmanaged() || window->isDock() || window->isDesktop() || window->belongsToDesktop());
1565}
1566
1567void Workspace::setShowingDesktop(bool showing, bool animated)
1568{
1569 const bool changed = showing != showing_desktop;
1570 if (rootInfo() && changed) {
1571 rootInfo()->setShowingDesktop(showing);
1572 }
1573 showing_desktop = showing;
1574
1575 for (int i = stacking_order.count() - 1; i > -1; --i) {
1576 auto window = stacking_order.at(i);
1577 if (window->isDeleted()) {
1578 continue;
1579 }
1580 if (breaksShowingDesktop(window)) {
1581 window->setHiddenByShowDesktop(showing_desktop);
1582 }
1583 }
1584
1585 if (showing_desktop) {
1586 Window *desktop = findDesktop(true, VirtualDesktopManager::self()->currentDesktop());
1587 if (desktop) {
1588 requestFocus(desktop);
1589 }
1590 } else if (!showing_desktop && changed) {
1591 const auto window = m_focusChain->getForActivation(VirtualDesktopManager::self()->currentDesktop());
1592 if (window) {
1593 activateWindow(window);
1594 }
1595 }
1596 if (changed) {
1597 Q_EMIT showingDesktopChanged(showing, animated);
1598 }
1599}
1600
1601void Workspace::disableGlobalShortcutsForClient(bool disable)
1602{
1603 if (m_globalShortcutsDisabledForWindow == disable) {
1604 return;
1605 }
1606 QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.kglobalaccel"),
1607 QStringLiteral("/kglobalaccel"),
1608 QStringLiteral("org.kde.KGlobalAccel"),
1609 QStringLiteral("blockGlobalShortcuts"));
1610 message.setArguments(QList<QVariant>() << disable);
1611 QDBusConnection::sessionBus().asyncCall(message);
1612
1613 m_globalShortcutsDisabledForWindow = disable;
1614 // Update also Meta+LMB actions etc.
1615 for (auto it = m_windows.constBegin(); it != m_windows.constEnd(); ++it) {
1616 (*it)->updateMouseGrab();
1617 }
1618}
1619
1620QString Workspace::supportInformation() const
1621{
1622 QString support;
1623 const QString yes = QStringLiteral("yes\n");
1624 const QString no = QStringLiteral("no\n");
1625
1626 support.append(ki18nc("Introductory text shown in the support information.",
1627 "KWin Support Information:\n"
1628 "The following information should be used when requesting support on e.g. https://discuss.kde.org.\n"
1629 "It provides information about the currently running instance, which options are used,\n"
1630 "what OpenGL driver and which effects are running.\n"
1631 "Please post the information provided underneath this introductory text to a paste bin service\n"
1632 "like https://paste.kde.org instead of pasting into support threads.\n")
1633 .toString());
1634 support.append(QStringLiteral("\n==========================\n\n"));
1635 // all following strings are intended for support. They need to be pasted to e.g forums.kde.org
1636 // it is expected that the support will happen in English language or that the people providing
1637 // help understand English. Because of that all texts are not translated
1638 support.append(QStringLiteral("Version\n"));
1639 support.append(QStringLiteral("=======\n"));
1640 support.append(QStringLiteral("KWin version: "));
1641 support.append(KWIN_VERSION_STRING);
1642 support.append(QStringLiteral("\n"));
1643 support.append(QStringLiteral("Qt Version: "));
1644 support.append(QString::fromUtf8(qVersion()));
1645 support.append(QStringLiteral("\n"));
1646 support.append(QStringLiteral("Qt compile version: %1\n").arg(QStringLiteral(QT_VERSION_STR)));
1647 support.append(QStringLiteral("XCB compile version: %1\n\n").arg(XCB_VERSION_STRING));
1648 support.append(QStringLiteral("Operation Mode: "));
1649 switch (kwinApp()->operationMode()) {
1650 case Application::OperationModeX11:
1651 support.append(QStringLiteral("X11 only"));
1652 break;
1653 case Application::OperationModeWaylandOnly:
1654 support.append(QStringLiteral("Wayland Only"));
1655 break;
1656 case Application::OperationModeXwayland:
1657 support.append(QStringLiteral("Xwayland"));
1658 break;
1659 }
1660 support.append(QStringLiteral("\n\n"));
1661
1662 support.append(QStringLiteral("Build Options\n"));
1663 support.append(QStringLiteral("=============\n"));
1664
1665 support.append(QStringLiteral("KWIN_BUILD_DECORATIONS: "));
1666 support.append(KWIN_BUILD_DECORATIONS ? yes : no);
1667 support.append(QStringLiteral("KWIN_BUILD_TABBOX: "));
1668 support.append(KWIN_BUILD_TABBOX ? yes : no);
1669 support.append(QStringLiteral("KWIN_BUILD_ACTIVITIES: "));
1670 support.append(KWIN_BUILD_ACTIVITIES ? yes : no);
1671 support.append(QStringLiteral("HAVE_X11_XCB: "));
1672 support.append(HAVE_X11_XCB ? yes : no);
1673 support.append(QStringLiteral("HAVE_GLX: "));
1674 support.append(HAVE_GLX ? yes : no);
1675 support.append(QStringLiteral("\n"));
1676
1677 if (auto c = kwinApp()->x11Connection()) {
1678 support.append(QStringLiteral("X11\n"));
1679 support.append(QStringLiteral("===\n"));
1680 auto x11setup = xcb_get_setup(c);
1681 support.append(QStringLiteral("Vendor: %1\n").arg(QString::fromUtf8(QByteArray::fromRawData(xcb_setup_vendor(x11setup), xcb_setup_vendor_length(x11setup)))));
1682 support.append(QStringLiteral("Vendor Release: %1\n").arg(x11setup->release_number));
1683 support.append(QStringLiteral("Protocol Version/Revision: %1/%2\n").arg(x11setup->protocol_major_version).arg(x11setup->protocol_minor_version));
1684 const auto extensions = Xcb::Extensions::self()->extensions();
1685 for (const auto &e : extensions) {
1686 support.append(QStringLiteral("%1: %2; Version: 0x%3\n")
1687 .arg(QString::fromUtf8(e.name), e.present ? yes.trimmed() : no.trimmed(), QString::number(e.version, 16)));
1688 }
1689 support.append(QStringLiteral("\n"));
1690 }
1691
1692 if (m_decorationBridge) {
1693 support.append(QStringLiteral("Decoration\n"));
1694 support.append(QStringLiteral("==========\n"));
1695 support.append(m_decorationBridge->supportInformation());
1696 support.append(QStringLiteral("\n"));
1697 }
1698 support.append(QStringLiteral("Output backend\n"));
1699 support.append(QStringLiteral("==============\n"));
1700 support.append(kwinApp()->outputBackend()->supportInformation());
1701 support.append(QStringLiteral("\n"));
1702
1703 const Cursor *cursor = Cursors::self()->mouse();
1704 support.append(QLatin1String("Cursor\n"));
1705 support.append(QLatin1String("======\n"));
1706 support.append(QLatin1String("themeName: ") + cursor->themeName() + QLatin1Char('\n'));
1707 support.append(QLatin1String("themeSize: ") + QString::number(cursor->themeSize()) + QLatin1Char('\n'));
1708 support.append(QLatin1Char('\n'));
1709
1710 support.append(QStringLiteral("Options\n"));
1711 support.append(QStringLiteral("=======\n"));
1712 const QMetaObject *metaOptions = options->metaObject();
1713 auto printProperty = [](const QVariant &variant) {
1714 if (variant.type() == QVariant::Size) {
1715 const QSize &s = variant.toSize();
1716 return QStringLiteral("%1x%2").arg(s.width()).arg(s.height());
1717 }
1718 if (QLatin1String(variant.typeName()) == QLatin1String("KWin::OpenGLPlatformInterface") || QLatin1String(variant.typeName()) == QLatin1String("KWin::Options::WindowOperation")) {
1719 return QString::number(variant.toInt());
1720 }
1721 return variant.toString();
1722 };
1723 for (int i = 0; i < metaOptions->propertyCount(); ++i) {
1724 const QMetaProperty property = metaOptions->property(i);
1725 if (QLatin1String(property.name()) == QLatin1String("objectName")) {
1726 continue;
1727 }
1728 support.append(QStringLiteral("%1: %2\n").arg(property.name(), printProperty(options->property(property.name()))));
1729 }
1730 support.append(QStringLiteral("\nScreen Edges\n"));
1731 support.append(QStringLiteral("============\n"));
1732 const QMetaObject *metaScreenEdges = m_screenEdges->metaObject();
1733 for (int i = 0; i < metaScreenEdges->propertyCount(); ++i) {
1734 const QMetaProperty property = metaScreenEdges->property(i);
1735 if (QLatin1String(property.name()) == QLatin1String("objectName")) {
1736 continue;
1737 }
1738 support.append(QStringLiteral("%1: %2\n").arg(property.name(), printProperty(m_screenEdges->property(property.name()))));
1739 }
1740 support.append(QStringLiteral("\nScreens\n"));
1741 support.append(QStringLiteral("=======\n"));
1742 support.append(QStringLiteral("Active screen follows mouse: "));
1743 if (options->activeMouseScreen()) {
1744 support.append(QStringLiteral(" yes\n"));
1745 } else {
1746 support.append(QStringLiteral(" no\n"));
1747 }
1748 const QList<Output *> outputs = kwinApp()->outputBackend()->outputs();
1749 support.append(QStringLiteral("Number of Screens: %1\n\n").arg(outputs.count()));
1750 for (int i = 0; i < outputs.count(); ++i) {
1751 const auto output = outputs[i];
1752 const QRect geo = outputs[i]->geometry();
1753 support.append(QStringLiteral("Screen %1:\n").arg(i));
1754 support.append(QStringLiteral("---------\n"));
1755 support.append(QStringLiteral("Name: %1\n").arg(output->name()));
1756 support.append(QStringLiteral("Enabled: %1\n").arg(output->isEnabled()));
1757 if (output->isEnabled()) {
1758 support.append(QStringLiteral("Geometry: %1,%2,%3x%4\n")
1759 .arg(geo.x())
1760 .arg(geo.y())
1761 .arg(geo.width())
1762 .arg(geo.height()));
1763 support.append(QStringLiteral("Scale: %1\n").arg(output->scale()));
1764 support.append(QStringLiteral("Refresh Rate: %1\n").arg(output->refreshRate()));
1765 QString vrr = QStringLiteral("incapable");
1766 if (output->capabilities() & Output::Capability::Vrr) {
1767 switch (output->vrrPolicy()) {
1768 case VrrPolicy::Never:
1769 vrr = QStringLiteral("never");
1770 break;
1771 case VrrPolicy::Always:
1772 vrr = QStringLiteral("always");
1773 break;
1774 case VrrPolicy::Automatic:
1775 vrr = QStringLiteral("automatic");
1776 break;
1777 }
1778 }
1779 support.append(QStringLiteral("Adaptive Sync: %1\n").arg(vrr));
1780 }
1781 }
1782 support.append(QStringLiteral("\nCompositing\n"));
1783 support.append(QStringLiteral("===========\n"));
1784 if (effects) {
1785 support.append(QStringLiteral("Compositing is active\n"));
1786 switch (effects->compositingType()) {
1787 case OpenGLCompositing: {
1788 GLPlatform *platform = GLPlatform::instance();
1789 if (platform->isGLES()) {
1790 support.append(QStringLiteral("Compositing Type: OpenGL ES 2.0\n"));
1791 } else {
1792 support.append(QStringLiteral("Compositing Type: OpenGL\n"));
1793 }
1794 support.append(QStringLiteral("OpenGL vendor string: ") + QString::fromUtf8(platform->glVendorString()) + QStringLiteral("\n"));
1795 support.append(QStringLiteral("OpenGL renderer string: ") + QString::fromUtf8(platform->glRendererString()) + QStringLiteral("\n"));
1796 support.append(QStringLiteral("OpenGL version string: ") + QString::fromUtf8(platform->glVersionString()) + QStringLiteral("\n"));
1797 support.append(QStringLiteral("OpenGL platform interface: "));
1798 switch (platform->platformInterface()) {
1800 support.append(QStringLiteral("GLX"));
1801 break;
1803 support.append(QStringLiteral("EGL"));
1804 break;
1805 default:
1806 support.append(QStringLiteral("UNKNOWN"));
1807 }
1808 support.append(QStringLiteral("\n"));
1809
1810 support.append(QStringLiteral("OpenGL shading language version string: ") + QString::fromUtf8(platform->glShadingLanguageVersionString()) + QStringLiteral("\n"));
1811
1812 support.append(QStringLiteral("Driver: ") + GLPlatform::driverToString(platform->driver()) + QStringLiteral("\n"));
1813 if (!platform->isMesaDriver()) {
1814 support.append(QStringLiteral("Driver version: ") + platform->driverVersion().toString() + QStringLiteral("\n"));
1815 }
1816
1817 support.append(QStringLiteral("GPU class: ") + GLPlatform::chipClassToString(platform->chipClass()) + QStringLiteral("\n"));
1818
1819 support.append(QStringLiteral("OpenGL version: ") + platform->glVersion().toString() + QStringLiteral("\n"));
1820
1821 support.append(QStringLiteral("GLSL version: ") + platform->glslVersion().toString() + QStringLiteral("\n"));
1822
1823 if (platform->isMesaDriver()) {
1824 support.append(QStringLiteral("Mesa version: ") + platform->mesaVersion().toString() + QStringLiteral("\n"));
1825 }
1826 if (auto xVersion = Xcb::xServerVersion(); xVersion.isValid()) {
1827 support.append(QStringLiteral("X server version: ") + xVersion.toString() + QStringLiteral("\n"));
1828 }
1829 if (auto kernelVersion = linuxKernelVersion(); kernelVersion.isValid()) {
1830 support.append(QStringLiteral("Linux kernel version: ") + kernelVersion.toString() + QStringLiteral("\n"));
1831 }
1832
1833 support.append(QStringLiteral("Direct rendering: "));
1834 support.append(QStringLiteral("Requires strict binding: "));
1835 if (!platform->isLooseBinding()) {
1836 support.append(QStringLiteral("yes\n"));
1837 } else {
1838 support.append(QStringLiteral("no\n"));
1839 }
1840 support.append(QStringLiteral("Virtual Machine: "));
1841 if (platform->isVirtualMachine()) {
1842 support.append(QStringLiteral(" yes\n"));
1843 } else {
1844 support.append(QStringLiteral(" no\n"));
1845 }
1846
1847 support.append(QStringLiteral("OpenGL 2 Shaders are used\n"));
1848 break;
1849 }
1851 support.append("Compositing Type: QPainter\n");
1852 break;
1853 case NoCompositing:
1854 default:
1855 support.append(QStringLiteral("Something is really broken, neither OpenGL nor QPainter is used"));
1856 }
1857 support.append(QStringLiteral("\nLoaded Effects:\n"));
1858 support.append(QStringLiteral("---------------\n"));
1859 const auto loadedEffects = effects->loadedEffects();
1860 for (const QString &effect : loadedEffects) {
1861 support.append(effect + QStringLiteral("\n"));
1862 }
1863 support.append(QStringLiteral("\nCurrently Active Effects:\n"));
1864 support.append(QStringLiteral("-------------------------\n"));
1865 const auto activeEffects = effects->activeEffects();
1866 for (const QString &effect : activeEffects) {
1867 support.append(effect + QStringLiteral("\n"));
1868 }
1869 support.append(QStringLiteral("\nEffect Settings:\n"));
1870 support.append(QStringLiteral("----------------\n"));
1871 for (const QString &effect : loadedEffects) {
1872 support.append(effects->supportInformation(effect));
1873 support.append(QStringLiteral("\n"));
1874 }
1875 support.append(QLatin1String("\nLoaded Plugins:\n"));
1876 support.append(QLatin1String("---------------\n"));
1877 QStringList loadedPlugins = kwinApp()->pluginManager()->loadedPlugins();
1878 loadedPlugins.sort();
1879 for (const QString &plugin : std::as_const(loadedPlugins)) {
1880 support.append(plugin + QLatin1Char('\n'));
1881 }
1882 support.append(QLatin1String("\nAvailable Plugins:\n"));
1883 support.append(QLatin1String("------------------\n"));
1884 QStringList availablePlugins = kwinApp()->pluginManager()->availablePlugins();
1885 availablePlugins.sort();
1886 for (const QString &plugin : std::as_const(availablePlugins)) {
1887 support.append(plugin + QLatin1Char('\n'));
1888 }
1889 } else {
1890 support.append(QStringLiteral("Compositing is not active\n"));
1891 }
1892 return support;
1893}
1894
1895void Workspace::forEachClient(std::function<void(X11Window *)> func)
1896{
1897 for (Window *window : std::as_const(m_windows)) {
1898 X11Window *x11Window = qobject_cast<X11Window *>(window);
1899 if (x11Window && !x11Window->isUnmanaged()) {
1900 func(x11Window);
1901 }
1902 }
1903}
1904
1905X11Window *Workspace::findClient(std::function<bool(const X11Window *)> func) const
1906{
1907 for (Window *window : std::as_const(m_windows)) {
1908 X11Window *x11Window = qobject_cast<X11Window *>(window);
1909 if (x11Window && !x11Window->isUnmanaged() && func(x11Window)) {
1910 return x11Window;
1911 }
1912 }
1913 return nullptr;
1914}
1915
1916X11Window *Workspace::findUnmanaged(std::function<bool(const X11Window *)> func) const
1917{
1918 for (Window *window : m_windows) {
1919 X11Window *x11Window = qobject_cast<X11Window *>(window);
1920 if (x11Window && x11Window->isUnmanaged() && func(x11Window)) {
1921 return x11Window;
1922 }
1923 }
1924 return nullptr;
1925}
1926
1927X11Window *Workspace::findUnmanaged(xcb_window_t w) const
1928{
1929 return findUnmanaged([w](const X11Window *u) {
1930 return u->window() == w;
1931 });
1932}
1933
1934X11Window *Workspace::findClient(Predicate predicate, xcb_window_t w) const
1935{
1936 switch (predicate) {
1937 case Predicate::WindowMatch:
1938 return findClient([w](const X11Window *c) {
1939 return c->window() == w;
1940 });
1941 case Predicate::WrapperIdMatch:
1942 return findClient([w](const X11Window *c) {
1943 return c->wrapperId() == w;
1944 });
1945 case Predicate::FrameIdMatch:
1946 return findClient([w](const X11Window *c) {
1947 return c->frameId() == w;
1948 });
1949 case Predicate::InputIdMatch:
1950 return findClient([w](const X11Window *c) {
1951 return c->inputId() == w;
1952 });
1953 }
1954 return nullptr;
1955}
1956
1957Window *Workspace::findWindow(std::function<bool(const Window *)> func) const
1958{
1959 return Window::findInList(m_windows, func);
1960}
1961
1962Window *Workspace::findWindow(const QUuid &internalId) const
1963{
1964 return findWindow([internalId](const KWin::Window *l) -> bool {
1965 return internalId == l->internalId();
1966 });
1967}
1968
1969void Workspace::forEachWindow(std::function<void(Window *)> func)
1970{
1971 std::for_each(m_windows.constBegin(), m_windows.constEnd(), func);
1972}
1973
1974bool Workspace::hasWindow(const Window *c)
1975{
1976 return findWindow([&c](const Window *test) {
1977 return test == c;
1978 })
1979 != nullptr;
1980}
1981
1982Window *Workspace::findInternal(QWindow *w) const
1983{
1984 if (!w) {
1985 return nullptr;
1986 }
1987 if (kwinApp()->operationMode() == Application::OperationModeX11) {
1988 return findUnmanaged(w->winId());
1989 }
1990 for (Window *window : m_windows) {
1991 if (InternalWindow *internal = qobject_cast<InternalWindow *>(window)) {
1992 if (internal->handle() == w) {
1993 return internal;
1994 }
1995 }
1996 }
1997 return nullptr;
1998}
1999
2000void Workspace::setWasUserInteraction()
2001{
2002 if (was_user_interaction) {
2003 return;
2004 }
2005 was_user_interaction = true;
2006 // might be called from within the filter, so delay till we now the filter returned
2007 QTimer::singleShot(0, this,
2008 [this] {
2009 m_wasUserInteractionFilter.reset();
2010 });
2011}
2012
2013void Workspace::updateTabbox()
2014{
2015#if KWIN_BUILD_TABBOX
2016 // Need to reset the client model even if the task switcher is hidden otherwise there
2017 // might be dangling pointers. Consider rewriting client model logic!
2018 m_tabbox->reset(true);
2019#endif
2020}
2021
2022void Workspace::addInternalWindow(InternalWindow *window)
2023{
2024 Q_ASSERT(!m_windows.contains(window));
2025 m_windows.append(window);
2026 addToStack(window);
2027
2028 setupWindowConnections(window);
2029 window->updateLayer();
2030
2031 if (window->isPlaceable()) {
2032 const QRectF area = clientArea(PlacementArea, window, workspace()->activeOutput());
2033 m_placement->place(window, area);
2034 }
2035
2036 updateStackingOrder(true);
2037 Q_EMIT windowAdded(window);
2038}
2039
2040void Workspace::removeInternalWindow(InternalWindow *window)
2041{
2042 m_windows.removeOne(window);
2043
2044 updateStackingOrder();
2045 Q_EMIT windowRemoved(window);
2046}
2047
2048void Workspace::setInitialDesktop(int desktop)
2049{
2050 m_initialDesktop = desktop;
2051}
2052
2053Group *Workspace::findGroup(xcb_window_t leader) const
2054{
2055 Q_ASSERT(leader != XCB_WINDOW_NONE);
2056 for (auto it = groups.constBegin(); it != groups.constEnd(); ++it) {
2057 if ((*it)->leader() == leader) {
2058 return *it;
2059 }
2060 }
2061 return nullptr;
2062}
2063
2064// Window is group transient, but has no group set. Try to find
2065// group with windows with the same client leader.
2066Group *Workspace::findClientLeaderGroup(const X11Window *window) const
2067{
2068 Group *ret = nullptr;
2069 for (auto it = m_windows.constBegin(); it != m_windows.constEnd(); ++it) {
2070 X11Window *candidate = qobject_cast<X11Window *>(*it);
2071 if (!candidate || candidate == window) {
2072 continue;
2073 }
2074 if (candidate->wmClientLeader() == window->wmClientLeader()) {
2075 if (ret == nullptr || ret == candidate->group()) {
2076 ret = candidate->group();
2077 } else {
2078 // There are already two groups with the same client leader.
2079 // This most probably means the app uses group transients without
2080 // setting group for its windows. Merging the two groups is a bad
2081 // hack, but there's no really good solution for this case.
2082 QList<X11Window *> old_group = candidate->group()->members();
2083 // old_group autodeletes when being empty
2084 for (int pos = 0; pos < old_group.count(); ++pos) {
2085 X11Window *tmp = old_group[pos];
2086 if (tmp != window) {
2087 tmp->changeClientLeaderGroup(ret);
2088 }
2089 }
2090 }
2091 }
2092 }
2093 return ret;
2094}
2095
2096void Workspace::updateMinimizedOfTransients(Window *window)
2097{
2098 // if mainwindow is minimized or shaded, minimize transients too
2099 if (window->isMinimized()) {
2100 for (auto it = window->transients().constBegin(); it != window->transients().constEnd(); ++it) {
2101 if ((*it)->isModal()) {
2102 continue; // there's no reason to hide modal dialogs with the main window
2103 }
2104 // but to keep them to eg. watch progress or whatever
2105 if (!(*it)->isMinimized()) {
2106 (*it)->setMinimized(true);
2107 updateMinimizedOfTransients((*it));
2108 }
2109 }
2110 if (window->isModal()) { // if a modal dialog is minimized, minimize its mainwindow too
2111 const auto windows = window->mainWindows();
2112 for (Window *main : std::as_const(windows)) {
2113 main->setMinimized(true);
2114 }
2115 }
2116 } else {
2117 // else unmiminize the transients
2118 for (auto it = window->transients().constBegin(); it != window->transients().constEnd(); ++it) {
2119 if ((*it)->isMinimized()) {
2120 (*it)->setMinimized(false);
2121 updateMinimizedOfTransients((*it));
2122 }
2123 }
2124 if (window->isModal()) {
2125 const auto windows = window->mainWindows();
2126 for (Window *main : std::as_const(windows)) {
2127 main->setMinimized(false);
2128 }
2129 }
2130 }
2131}
2132
2136void Workspace::updateOnAllDesktopsOfTransients(Window *window)
2137{
2138 for (auto it = window->transients().constBegin(); it != window->transients().constEnd(); ++it) {
2139 if ((*it)->isOnAllDesktops() != window->isOnAllDesktops()) {
2140 (*it)->setOnAllDesktops(window->isOnAllDesktops());
2141 }
2142 }
2143}
2144
2145// A new window has been mapped. Check if it's not a mainwindow for some already existing transient window.
2146void Workspace::checkTransients(xcb_window_t w)
2147{
2148 for (auto it = m_windows.constBegin(); it != m_windows.constEnd(); ++it) {
2149 if (X11Window *x11Window = qobject_cast<X11Window *>(*it)) {
2150 x11Window->checkTransient(w);
2151 }
2152 }
2153}
2154
2158void Workspace::desktopResized()
2159{
2160 m_placementTracker->inhibit();
2161
2162 const QRect oldGeometry = m_geometry;
2163 m_geometry = QRect();
2164 for (const Output *output : std::as_const(m_outputs)) {
2165 m_geometry = m_geometry.united(output->geometry());
2166 }
2167
2168 if (rootInfo()) {
2169 NETSize desktop_geometry;
2170 desktop_geometry.width = Xcb::toXNative(m_geometry.width());
2171 desktop_geometry.height = Xcb::toXNative(m_geometry.height());
2172 rootInfo()->setDesktopGeometry(desktop_geometry);
2173 }
2174
2175 updateClientArea();
2176
2177 const auto stack = stackingOrder();
2178 for (Window *window : stack) {
2179 window->setMoveResizeOutput(outputAt(window->moveResizeGeometry().center()));
2180 window->setOutput(outputAt(window->frameGeometry().center()));
2181 }
2182
2183 // restore cursor position
2184 const auto oldCursorOutput = std::find_if(m_oldScreenGeometries.cbegin(), m_oldScreenGeometries.cend(), [](const auto &geometry) {
2185 return exclusiveContains(geometry, Cursors::self()->mouse()->pos());
2186 });
2187 if (oldCursorOutput != m_oldScreenGeometries.cend()) {
2188 const Output *cursorOutput = oldCursorOutput.key();
2189 if (std::find(m_outputs.cbegin(), m_outputs.cend(), cursorOutput) != m_outputs.cend()) {
2190 const QRect oldGeometry = oldCursorOutput.value();
2191 const QRect newGeometry = cursorOutput->geometry();
2192 const QPointF relativePosition = Cursors::self()->mouse()->pos() - oldGeometry.topLeft();
2193 const QPointF newRelativePosition(newGeometry.width() * relativePosition.x() / float(oldGeometry.width()), newGeometry.height() * relativePosition.y() / float(oldGeometry.height()));
2194 Cursors::self()->mouse()->setPos(newGeometry.topLeft() + newRelativePosition);
2195 }
2196 }
2197
2198 saveOldScreenSizes(); // after updateClientArea(), so that one still uses the previous one
2199
2200 // TODO: emit a signal instead and remove the deep function calls into edges and effects
2201 m_screenEdges->recreateEdges();
2202
2203 m_placementTracker->uninhibit();
2204 m_placementTracker->restore(getPlacementTrackerHash());
2205 if (m_geometry != oldGeometry) {
2206 Q_EMIT geometryChanged();
2207 }
2208}
2209
2210void Workspace::saveOldScreenSizes()
2211{
2212 m_oldScreenGeometries.clear();
2213 for (const Output *output : std::as_const(m_outputs)) {
2214 m_oldScreenGeometries.insert(output, output->geometry());
2215 }
2216}
2217
2222static bool hasOffscreenXineramaStrut(Window *window)
2223{
2224 if (qobject_cast<X11Window *>(window)) {
2225 // Get strut as a QRegion
2226 QRegion region;
2227 region += window->strutRect(StrutAreaTop);
2228 region += window->strutRect(StrutAreaRight);
2229 region += window->strutRect(StrutAreaBottom);
2230 region += window->strutRect(StrutAreaLeft);
2231
2232 // Remove all visible areas so that only the invisible remain
2233 const auto outputs = workspace()->outputs();
2234 for (const Output *output : outputs) {
2235 region -= output->geometry();
2236 }
2237
2238 // If there's anything left then we have an offscreen strut
2239 return !region.isEmpty();
2240 }
2241
2242 return false;
2243}
2244
2245QRectF Workspace::adjustClientArea(Window *window, const QRectF &area) const
2246{
2247 QRectF adjustedArea = area;
2248
2249 QRectF strutLeft = window->strutRect(StrutAreaLeft);
2250 QRectF strutRight = window->strutRect(StrutAreaRight);
2251 QRectF strutTop = window->strutRect(StrutAreaTop);
2252 QRectF strutBottom = window->strutRect(StrutAreaBottom);
2253
2254 QRectF screenArea = clientArea(ScreenArea, window);
2255 if (qobject_cast<X11Window *>(window)) {
2256 // HACK: workarea handling is not xinerama aware, so if this strut
2257 // reserves place at a xinerama edge that's inside the virtual screen,
2258 // ignore the strut for workspace setting.
2259 if (area == QRect(QPoint(0, 0), m_geometry.size())) {
2260 if (strutLeft.left() < screenArea.left()) {
2261 strutLeft = QRect();
2262 }
2263 if (strutRight.right() > screenArea.right()) {
2264 strutRight = QRect();
2265 }
2266 if (strutTop.top() < screenArea.top()) {
2267 strutTop = QRect();
2268 }
2269 if (strutBottom.bottom() < screenArea.bottom()) {
2270 strutBottom = QRect();
2271 }
2272 }
2273 }
2274
2275 // Handle struts at xinerama edges that are inside the virtual screen.
2276 // They're given in virtual screen coordinates, make them affect only
2277 // their xinerama screen.
2278 strutLeft.setLeft(std::max(strutLeft.left(), screenArea.left()));
2279 strutRight.setRight(std::min(strutRight.right(), screenArea.right()));
2280 strutTop.setTop(std::max(strutTop.top(), screenArea.top()));
2281 strutBottom.setBottom(std::min(strutBottom.bottom(), screenArea.bottom()));
2282
2283 if (strutLeft.intersects(area)) {
2284 adjustedArea.setLeft(strutLeft.right());
2285 }
2286 if (strutRight.intersects(area)) {
2287 adjustedArea.setRight(strutRight.left());
2288 }
2289 if (strutTop.intersects(area)) {
2290 adjustedArea.setTop(strutTop.bottom());
2291 }
2292 if (strutBottom.intersects(area)) {
2293 adjustedArea.setBottom(strutBottom.top());
2294 }
2295
2296 return adjustedArea;
2297}
2298
2308void Workspace::updateClientArea()
2309{
2310 const QList<VirtualDesktop *> desktops = VirtualDesktopManager::self()->desktops();
2311
2312 QHash<const VirtualDesktop *, QRectF> workAreas;
2313 QHash<const VirtualDesktop *, StrutRects> restrictedAreas;
2314 QHash<const VirtualDesktop *, QHash<const Output *, QRectF>> screenAreas;
2315
2316 for (const VirtualDesktop *desktop : desktops) {
2317 workAreas[desktop] = m_geometry;
2318
2319 for (const Output *output : std::as_const(m_outputs)) {
2320 screenAreas[desktop][output] = output->geometryF();
2321 }
2322 }
2323
2324 for (Window *window : std::as_const(m_windows)) {
2325 if (!window->hasStrut()) {
2326 continue;
2327 }
2328 QRectF r = adjustClientArea(window, m_geometry);
2329
2330 // This happens sometimes when the workspace size changes and the
2331 // struted windows haven't repositioned yet
2332 if (!r.isValid()) {
2333 continue;
2334 }
2335 // sanity check that a strut doesn't exclude a complete screen geometry
2336 // this is a violation to EWMH, as KWin just ignores the strut
2337 for (const Output *output : std::as_const(m_outputs)) {
2338 if (!r.intersects(output->geometry())) {
2339 qCDebug(KWIN_CORE) << "Adjusted client area would exclude a complete screen, ignore";
2340 r = m_geometry;
2341 break;
2342 }
2343 }
2344 StrutRects strutRegion = window->strutRects();
2345 const QRect clientsScreenRect = window->output()->geometry();
2346 for (int i = strutRegion.size() - 1; i >= 0; --i) {
2347 const StrutRect clipped = StrutRect(strutRegion[i].intersected(clientsScreenRect), strutRegion[i].area());
2348 if (clipped.isEmpty()) {
2349 strutRegion.removeAt(i);
2350 } else {
2351 strutRegion[i] = clipped;
2352 }
2353 }
2354
2355 // Ignore offscreen xinerama struts. These interfere with the larger monitors on the setup
2356 // and should be ignored so that applications that use the work area to work out where
2357 // windows can go can use the entire visible area of the larger monitors.
2358 // This goes against the EWMH description of the work area but it is a toss up between
2359 // having unusable sections of the screen (Which can be quite large with newer monitors)
2360 // or having some content appear offscreen (Relatively rare compared to other).
2361 bool hasOffscreenStrut = hasOffscreenXineramaStrut(window);
2362
2363 const auto vds = window->isOnAllDesktops() ? desktops : window->desktops();
2364 for (VirtualDesktop *vd : vds) {
2365 if (!hasOffscreenStrut) {
2366 workAreas[vd] &= r;
2367 }
2368 restrictedAreas[vd] += strutRegion;
2369 for (Output *output : std::as_const(m_outputs)) {
2370 const auto geo = screenAreas[vd][output].intersected(adjustClientArea(window, output->geometryF()));
2371 // ignore the geometry if it results in the screen getting removed completely
2372 if (!geo.isEmpty()) {
2373 screenAreas[vd][output] = geo;
2374 }
2375 }
2376 }
2377 }
2378
2379 if (m_workAreas != workAreas || m_restrictedAreas != restrictedAreas || m_screenAreas != screenAreas) {
2380 m_workAreas = workAreas;
2381 m_screenAreas = screenAreas;
2382
2383 m_inUpdateClientArea = true;
2384 m_oldRestrictedAreas = m_restrictedAreas;
2385 m_restrictedAreas = restrictedAreas;
2386
2387 if (rootInfo()) {
2388 for (VirtualDesktop *desktop : desktops) {
2389 const QRectF &workArea = m_workAreas[desktop];
2390 NETRect r(Xcb::toXNative(workArea));
2391 rootInfo()->setWorkArea(desktop->x11DesktopNumber(), r);
2392 }
2393 }
2394
2395 for (auto it = m_windows.constBegin(); it != m_windows.constEnd(); ++it) {
2396 if ((*it)->isClient()) {
2397 (*it)->checkWorkspacePosition();
2398 }
2399 }
2400
2401 m_oldRestrictedAreas.clear(); // reset, no longer valid or needed
2402 m_inUpdateClientArea = false;
2403 }
2404}
2405
2411QRectF Workspace::clientArea(clientAreaOption opt, const Output *output, const VirtualDesktop *desktop) const
2412{
2413 switch (opt) {
2414 case MaximizeArea:
2415 case PlacementArea:
2416 if (auto desktopIt = m_screenAreas.constFind(desktop); desktopIt != m_screenAreas.constEnd()) {
2417 if (auto outputIt = desktopIt->constFind(output); outputIt != desktopIt->constEnd()) {
2418 return *outputIt;
2419 }
2420 }
2421 return output->geometryF();
2422 case MaximizeFullArea:
2423 case FullScreenArea:
2424 case MovementArea:
2425 case ScreenArea:
2426 return output->geometryF();
2427 case WorkArea:
2428 return m_workAreas.value(desktop, m_geometry);
2429 case FullArea:
2430 return m_geometry;
2431 default:
2432 Q_UNREACHABLE();
2433 }
2434}
2435
2436QRectF Workspace::clientArea(clientAreaOption opt, const Window *window) const
2437{
2438 return clientArea(opt, window, window->output());
2439}
2440
2441QRectF Workspace::clientArea(clientAreaOption opt, const Window *window, const Output *output) const
2442{
2443 const VirtualDesktop *desktop;
2444 if (window->isOnCurrentDesktop()) {
2445 desktop = VirtualDesktopManager::self()->currentDesktop();
2446 } else {
2447 desktop = window->desktops().constLast();
2448 }
2449 return clientArea(opt, output, desktop);
2450}
2451
2452QRectF Workspace::clientArea(clientAreaOption opt, const Window *window, const QPointF &pos) const
2453{
2454 return clientArea(opt, window, outputAt(pos));
2455}
2456
2457QRect Workspace::geometry() const
2458{
2459 return m_geometry;
2460}
2461
2462StrutRects Workspace::restrictedMoveArea(const VirtualDesktop *desktop, StrutAreas areas) const
2463{
2464 const StrutRects strut = m_restrictedAreas.value(desktop);
2465 if (areas == StrutAreaAll) {
2466 return strut;
2467 }
2468
2469 StrutRects ret;
2470 ret.reserve(strut.size());
2471 for (const StrutRect &rect : strut) {
2472 if (rect.area() & areas) {
2473 ret.append(rect);
2474 }
2475 }
2476 return ret;
2477}
2478
2479bool Workspace::inUpdateClientArea() const
2480{
2481 return m_inUpdateClientArea;
2482}
2483
2484StrutRects Workspace::previousRestrictedMoveArea(const VirtualDesktop *desktop, StrutAreas areas) const
2485{
2486 const StrutRects strut = m_oldRestrictedAreas.value(desktop);
2487 if (areas == StrutAreaAll) {
2488 return strut;
2489 }
2490
2491 StrutRects ret;
2492 ret.reserve(strut.size());
2493 for (const StrutRect &rect : strut) {
2494 if (rect.area() & areas) {
2495 ret.append(rect);
2496 }
2497 }
2498 return ret;
2499}
2500
2501QHash<const Output *, QRect> Workspace::previousScreenSizes() const
2502{
2503 return m_oldScreenGeometries;
2504}
2505
2506Output *Workspace::xineramaIndexToOutput(int index) const
2507{
2508 xcb_connection_t *connection = kwinApp()->x11Connection();
2509 if (!connection) {
2510 return nullptr;
2511 }
2512
2513 const UniqueCPtr<xcb_xinerama_is_active_reply_t> active{xcb_xinerama_is_active_reply(connection, xcb_xinerama_is_active(connection), nullptr)};
2514 if (!active || !active->state) {
2515 return nullptr;
2516 }
2517
2518 const UniqueCPtr<xcb_xinerama_query_screens_reply_t> screens(xcb_xinerama_query_screens_reply(connection, xcb_xinerama_query_screens(connection), nullptr));
2519 if (!screens) {
2520 return nullptr;
2521 }
2522
2523 const int infoCount = xcb_xinerama_query_screens_screen_info_length(screens.get());
2524 if (index >= infoCount) {
2525 return nullptr;
2526 }
2527
2528 const xcb_xinerama_screen_info_t *infos = xcb_xinerama_query_screens_screen_info(screens.get());
2529 const QRect needle(infos[index].x_org, infos[index].y_org, infos[index].width, infos[index].height);
2530
2531 for (Output *output : std::as_const(m_outputs)) {
2532 if (Xcb::toXNative(output->geometryF()) == needle) {
2533 return output;
2534 }
2535 }
2536
2537 return nullptr;
2538}
2539
2540void Workspace::setOutputOrder(const QList<Output *> &order)
2541{
2542 if (m_outputOrder != order) {
2543 m_outputOrder = order;
2544 Q_EMIT outputOrderChanged();
2545 }
2546}
2547
2548QList<Output *> Workspace::outputOrder() const
2549{
2550 return m_outputOrder;
2551}
2552
2553Output *Workspace::activeOutput() const
2554{
2555 if (options->activeMouseScreen()) {
2556 if (m_activeCursorOutput) {
2557 return m_activeCursorOutput;
2558 } else {
2559 return outputAt(Cursors::self()->mouse()->pos());
2560 }
2561 }
2562
2563 if (m_activeWindow && !m_activeWindow->isOnOutput(m_activeOutput)) {
2564 return m_activeWindow->output();
2565 }
2566
2567 return m_activeOutput;
2568}
2569
2570void Workspace::setActiveOutput(Output *output)
2571{
2572 m_activeOutput = output;
2573}
2574
2575void Workspace::setActiveOutput(const QPointF &pos)
2576{
2577 setActiveOutput(outputAt(pos));
2578}
2579
2580void Workspace::setActiveCursorOutput(Output *output)
2581{
2582 m_activeCursorOutput = output;
2583}
2584
2585void Workspace::setActiveCursorOutput(const QPointF &pos)
2586{
2587 setActiveCursorOutput(outputAt(pos));
2588}
2589
2599QPointF Workspace::adjustWindowPosition(Window *window, QPointF pos, bool unrestricted, double snapAdjust)
2600{
2601 QSizeF borderSnapZone(options->borderSnapZone(), options->borderSnapZone());
2602 QRectF maxRect;
2603 int guideMaximized = MaximizeRestore;
2604 if (window->maximizeMode() != MaximizeRestore) {
2605 maxRect = clientArea(MaximizeArea, window, pos + window->rect().center());
2606 QRectF geo = window->frameGeometry();
2607 if (window->maximizeMode() & MaximizeHorizontal && (geo.x() == maxRect.left() || geo.right() == maxRect.right())) {
2608 guideMaximized |= MaximizeHorizontal;
2609 borderSnapZone.setWidth(std::max(borderSnapZone.width() + 2, maxRect.width() / 16));
2610 }
2611 if (window->maximizeMode() & MaximizeVertical && (geo.y() == maxRect.top() || geo.bottom() == maxRect.bottom())) {
2612 guideMaximized |= MaximizeVertical;
2613 borderSnapZone.setHeight(std::max(borderSnapZone.height() + 2, maxRect.height() / 16));
2614 }
2615 }
2616
2617 if (options->windowSnapZone() || !borderSnapZone.isNull() || options->centerSnapZone()) {
2618
2619 const bool sOWO = options->isSnapOnlyWhenOverlapping();
2620 const Output *output = outputAt(pos + window->rect().center());
2621 if (maxRect.isNull()) {
2622 maxRect = clientArea(MaximizeArea, window, output);
2623 }
2624 const int xmin = maxRect.left();
2625 const int xmax = maxRect.right(); // desk size
2626 const int ymin = maxRect.top();
2627 const int ymax = maxRect.bottom();
2628
2629 const int cx(pos.x());
2630 const int cy(pos.y());
2631 const int cw(window->width());
2632 const int ch(window->height());
2633 const int rx(cx + cw);
2634 const int ry(cy + ch); // these don't change
2635
2636 int nx(cx), ny(cy); // buffers
2637 int deltaX(xmax);
2638 int deltaY(ymax); // minimum distance to other windows
2639
2640 int lx, ly, lrx, lry; // coords and size for the comparison window, l
2641
2642 // border snap
2643 const int borderXSnapZone = borderSnapZone.width() * snapAdjust; // snap trigger
2644 const int borderYSnapZone = borderSnapZone.height() * snapAdjust;
2645 if (borderXSnapZone > 0 || borderYSnapZone > 0) {
2646 if ((sOWO ? (cx < xmin) : true) && (std::abs(xmin - cx) < borderXSnapZone)) {
2647 deltaX = xmin - cx;
2648 nx = xmin;
2649 }
2650 if ((sOWO ? (rx > xmax) : true) && (std::abs(rx - xmax) < borderXSnapZone) && (std::abs(xmax - rx) < deltaX)) {
2651 deltaX = rx - xmax;
2652 nx = xmax - cw;
2653 }
2654
2655 if ((sOWO ? (cy < ymin) : true) && (std::abs(ymin - cy) < borderYSnapZone)) {
2656 deltaY = ymin - cy;
2657 ny = ymin;
2658 }
2659 if ((sOWO ? (ry > ymax) : true) && (std::abs(ry - ymax) < borderYSnapZone) && (std::abs(ymax - ry) < deltaY)) {
2660 deltaY = ry - ymax;
2661 ny = ymax - ch;
2662 }
2663 }
2664
2665 // windows snap
2666 const int windowSnapZone = options->windowSnapZone() * snapAdjust;
2667 if (windowSnapZone > 0) {
2668 for (auto l = m_windows.constBegin(); l != m_windows.constEnd(); ++l) {
2669 if ((*l) == window) {
2670 continue;
2671 }
2672 if ((*l)->isMinimized()) {
2673 continue;
2674 }
2675 if (!(*l)->isShown()) {
2676 continue;
2677 }
2678 if (!(*l)->isOnCurrentDesktop()) {
2679 continue; // wrong virtual desktop
2680 }
2681 if (!(*l)->isOnCurrentActivity()) {
2682 continue; // wrong activity
2683 }
2684 if ((*l)->isUnmanaged() || (*l)->isDesktop() || (*l)->isSplash() || (*l)->isNotification() || (*l)->isCriticalNotification() || (*l)->isOnScreenDisplay() || (*l)->isAppletPopup()) {
2685 continue;
2686 }
2687
2688 lx = (*l)->x();
2689 ly = (*l)->y();
2690 lrx = lx + (*l)->width();
2691 lry = ly + (*l)->height();
2692
2693 if (!(guideMaximized & MaximizeHorizontal) && (((cy <= lry) && (cy >= ly)) || ((ry >= ly) && (ry <= lry)) || ((cy <= ly) && (ry >= lry)))) {
2694 if ((sOWO ? (cx < lrx) : true) && (std::abs(lrx - cx) < windowSnapZone) && (std::abs(lrx - cx) < deltaX)) {
2695 deltaX = std::abs(lrx - cx);
2696 nx = lrx;
2697 }
2698 if ((sOWO ? (rx > lx) : true) && (std::abs(rx - lx) < windowSnapZone) && (std::abs(rx - lx) < deltaX)) {
2699 deltaX = std::abs(rx - lx);
2700 nx = lx - cw;
2701 }
2702 }
2703
2704 if (!(guideMaximized & MaximizeVertical) && (((cx <= lrx) && (cx >= lx)) || ((rx >= lx) && (rx <= lrx)) || ((cx <= lx) && (rx >= lrx)))) {
2705 if ((sOWO ? (cy < lry) : true) && (std::abs(lry - cy) < windowSnapZone) && (std::abs(lry - cy) < deltaY)) {
2706 deltaY = std::abs(lry - cy);
2707 ny = lry;
2708 }
2709 // if ( (std::abs( ry-ly ) < snap) && (std::abs( ry - ly ) < deltaY ))
2710 if ((sOWO ? (ry > ly) : true) && (std::abs(ry - ly) < windowSnapZone) && (std::abs(ry - ly) < deltaY)) {
2711 deltaY = std::abs(ry - ly);
2712 ny = ly - ch;
2713 }
2714 }
2715
2716 // Corner snapping
2717 if (!(guideMaximized & MaximizeVertical) && (nx == lrx || nx + cw == lx)) {
2718 if ((sOWO ? (ry > lry) : true) && (std::abs(lry - ry) < windowSnapZone) && (std::abs(lry - ry) < deltaY)) {
2719 deltaY = std::abs(lry - ry);
2720 ny = lry - ch;
2721 }
2722 if ((sOWO ? (cy < ly) : true) && (std::abs(cy - ly) < windowSnapZone) && (std::abs(cy - ly) < deltaY)) {
2723 deltaY = std::abs(cy - ly);
2724 ny = ly;
2725 }
2726 }
2727 if (!(guideMaximized & MaximizeHorizontal) && (ny == lry || ny + ch == ly)) {
2728 if ((sOWO ? (rx > lrx) : true) && (std::abs(lrx - rx) < windowSnapZone) && (std::abs(lrx - rx) < deltaX)) {
2729 deltaX = std::abs(lrx - rx);
2730 nx = lrx - cw;
2731 }
2732 if ((sOWO ? (cx < lx) : true) && (std::abs(cx - lx) < windowSnapZone) && (std::abs(cx - lx) < deltaX)) {
2733 deltaX = std::abs(cx - lx);
2734 nx = lx;
2735 }
2736 }
2737 }
2738 }
2739
2740 // center snap
2741 const int centerSnapZone = options->centerSnapZone() * snapAdjust;
2742 if (centerSnapZone > 0) {
2743 int diffX = std::abs((xmin + xmax) / 2 - (cx + cw / 2));
2744 int diffY = std::abs((ymin + ymax) / 2 - (cy + ch / 2));
2745 if (diffX < centerSnapZone && diffY < centerSnapZone && diffX < deltaX && diffY < deltaY) {
2746 // Snap to center of screen
2747 nx = (xmin + xmax) / 2 - cw / 2;
2748 ny = (ymin + ymax) / 2 - ch / 2;
2749 } else if (options->borderSnapZone() > 0) {
2750 // Enhance border snap
2751 if ((nx == xmin || nx == xmax - cw) && diffY < centerSnapZone && diffY < deltaY) {
2752 // Snap to vertical center on screen edge
2753 ny = (ymin + ymax) / 2 - ch / 2;
2754 } else if (((unrestricted ? ny == ymin : ny <= ymin) || ny == ymax - ch) && diffX < centerSnapZone && diffX < deltaX) {
2755 // Snap to horizontal center on screen edge
2756 nx = (xmin + xmax) / 2 - cw / 2;
2757 }
2758 }
2759 }
2760
2761 pos = QPoint(nx, ny);
2762 }
2763 return pos;
2764}
2765
2766QRectF Workspace::adjustWindowSize(Window *window, QRectF moveResizeGeom, Gravity gravity)
2767{
2768 // adapted from adjustWindowPosition on 29May2004
2769 // this function is called when resizing a window and will modify
2770 // the new dimensions to snap to other windows/borders if appropriate
2771 if (options->windowSnapZone() || options->borderSnapZone()) { // || options->centerSnapZone )
2772 const bool sOWO = options->isSnapOnlyWhenOverlapping();
2773
2774 const QRectF maxRect = clientArea(MovementArea, window, window->rect().center());
2775 const qreal xmin = maxRect.left();
2776 const qreal xmax = maxRect.right(); // desk size
2777 const qreal ymin = maxRect.top();
2778 const qreal ymax = maxRect.bottom();
2779
2780 const qreal cx(moveResizeGeom.left());
2781 const qreal cy(moveResizeGeom.top());
2782 const qreal rx(moveResizeGeom.right());
2783 const qreal ry(moveResizeGeom.bottom());
2784
2785 qreal newcx(cx), newcy(cy); // buffers
2786 qreal newrx(rx), newry(ry);
2787 qreal deltaX(xmax);
2788 qreal deltaY(ymax); // minimum distance to other windows
2789
2790 qreal lx, ly, lrx, lry; // coords and size for the comparison window, l
2791
2792 // border snap
2793 int snap = options->borderSnapZone(); // snap trigger
2794 if (snap) {
2795 deltaX = int(snap);
2796 deltaY = int(snap);
2797
2798#define SNAP_BORDER_TOP \
2799 if ((sOWO ? (newcy < ymin) : true) && (std::abs(ymin - newcy) < deltaY)) { \
2800 deltaY = std::abs(ymin - newcy); \
2801 newcy = ymin; \
2802 }
2803
2804#define SNAP_BORDER_BOTTOM \
2805 if ((sOWO ? (newry > ymax) : true) && (std::abs(ymax - newry) < deltaY)) { \
2806 deltaY = std::abs(ymax - newcy); \
2807 newry = ymax; \
2808 }
2809
2810#define SNAP_BORDER_LEFT \
2811 if ((sOWO ? (newcx < xmin) : true) && (std::abs(xmin - newcx) < deltaX)) { \
2812 deltaX = std::abs(xmin - newcx); \
2813 newcx = xmin; \
2814 }
2815
2816#define SNAP_BORDER_RIGHT \
2817 if ((sOWO ? (newrx > xmax) : true) && (std::abs(xmax - newrx) < deltaX)) { \
2818 deltaX = std::abs(xmax - newrx); \
2819 newrx = xmax; \
2820 }
2821 switch (gravity) {
2822 case Gravity::BottomRight:
2825 break;
2826 case Gravity::Right:
2828 break;
2829 case Gravity::Bottom:
2831 break;
2832 case Gravity::TopLeft:
2835 break;
2836 case Gravity::Left:
2838 break;
2839 case Gravity::Top:
2841 break;
2842 case Gravity::TopRight:
2845 break;
2846 case Gravity::BottomLeft:
2849 break;
2850 default:
2851 Q_UNREACHABLE();
2852 break;
2853 }
2854 }
2855
2856 // windows snap
2857 snap = options->windowSnapZone();
2858 if (snap) {
2859 deltaX = int(snap);
2860 deltaY = int(snap);
2861 for (auto l = m_windows.constBegin(); l != m_windows.constEnd(); ++l) {
2862 if ((*l)->isOnCurrentDesktop() && !(*l)->isMinimized() && !(*l)->isUnmanaged()
2863 && (*l) != window) {
2864 lx = (*l)->x();
2865 ly = (*l)->y();
2866 lrx = (*l)->x() + (*l)->width();
2867 lry = (*l)->y() + (*l)->height();
2868
2869#define WITHIN_HEIGHT (((newcy <= lry) && (newcy >= ly)) || ((newry >= ly) && (newry <= lry)) || ((newcy <= ly) && (newry >= lry)))
2870
2871#define WITHIN_WIDTH (((cx <= lrx) && (cx >= lx)) || ((rx >= lx) && (rx <= lrx)) || ((cx <= lx) && (rx >= lrx)))
2872
2873#define SNAP_WINDOW_TOP \
2874 if ((sOWO ? (newcy < lry) : true) \
2875 && WITHIN_WIDTH \
2876 && (std::abs(lry - newcy) < deltaY)) { \
2877 deltaY = std::abs(lry - newcy); \
2878 newcy = lry; \
2879 }
2880
2881#define SNAP_WINDOW_BOTTOM \
2882 if ((sOWO ? (newry > ly) : true) \
2883 && WITHIN_WIDTH \
2884 && (std::abs(ly - newry) < deltaY)) { \
2885 deltaY = std::abs(ly - newry); \
2886 newry = ly; \
2887 }
2888
2889#define SNAP_WINDOW_LEFT \
2890 if ((sOWO ? (newcx < lrx) : true) \
2891 && WITHIN_HEIGHT \
2892 && (std::abs(lrx - newcx) < deltaX)) { \
2893 deltaX = std::abs(lrx - newcx); \
2894 newcx = lrx; \
2895 }
2896
2897#define SNAP_WINDOW_RIGHT \
2898 if ((sOWO ? (newrx > lx) : true) \
2899 && WITHIN_HEIGHT \
2900 && (std::abs(lx - newrx) < deltaX)) { \
2901 deltaX = std::abs(lx - newrx); \
2902 newrx = lx; \
2903 }
2904
2905#define SNAP_WINDOW_C_TOP \
2906 if ((sOWO ? (newcy < ly) : true) \
2907 && (newcx == lrx || newrx == lx) \
2908 && std::abs(ly - newcy) < deltaY) { \
2909 deltaY = std::abs(ly - newcy); \
2910 newcy = ly; \
2911 }
2912
2913#define SNAP_WINDOW_C_BOTTOM \
2914 if ((sOWO ? (newry > lry) : true) \
2915 && (newcx == lrx || newrx == lx) \
2916 && std::abs(lry - newry) < deltaY) { \
2917 deltaY = std::abs(lry - newry); \
2918 newry = lry; \
2919 }
2920
2921#define SNAP_WINDOW_C_LEFT \
2922 if ((sOWO ? (newcx < lx) : true) \
2923 && (newcy == lry || newry == ly) \
2924 && std::abs(lx - newcx) < deltaX) { \
2925 deltaX = std::abs(lx - newcx); \
2926 newcx = lx; \
2927 }
2928
2929#define SNAP_WINDOW_C_RIGHT \
2930 if ((sOWO ? (newrx > lrx) : true) \
2931 && (newcy == lry || newry == ly) \
2932 && std::abs(lrx - newrx) < deltaX) { \
2933 deltaX = std::abs(lrx - newrx); \
2934 newrx = lrx; \
2935 }
2936
2937 switch (gravity) {
2938 case Gravity::BottomRight:
2943 break;
2944 case Gravity::Right:
2947 break;
2948 case Gravity::Bottom:
2951 break;
2952 case Gravity::TopLeft:
2957 break;
2958 case Gravity::Left:
2961 break;
2962 case Gravity::Top:
2965 break;
2966 case Gravity::TopRight:
2971 break;
2972 case Gravity::BottomLeft:
2977 break;
2978 default:
2979 Q_UNREACHABLE();
2980 break;
2981 }
2982 }
2983 }
2984 }
2985
2986 // center snap
2987 // snap = options->centerSnapZone;
2988 // if (snap)
2989 // {
2990 // // Don't resize snap to center as it interferes too much
2991 // // There are two ways of implementing this if wanted:
2992 // // 1) Snap only to the same points that the move snap does, and
2993 // // 2) Snap to the horizontal and vertical center lines of the screen
2994 // }
2995
2996 moveResizeGeom = QRectF(QPointF(newcx, newcy), QPointF(newrx, newry));
2997 }
2998 return moveResizeGeom;
2999}
3000
3004void Workspace::setMoveResizeWindow(Window *window)
3005{
3006 Q_ASSERT(!window || !m_moveResizeWindow); // Catch attempts to move a second
3007 // window while still moving the first one.
3008 m_moveResizeWindow = window;
3009 if (m_moveResizeWindow) {
3010 ++block_focus;
3011 } else {
3012 --block_focus;
3013 }
3014}
3015
3016// When kwin crashes, windows will not be gravitated back to their original position
3017// and will remain offset by the size of the decoration. So when restarting, fix this
3018// (the property with the size of the frame remains on the window after the crash).
3019void Workspace::fixPositionAfterCrash(xcb_window_t w, const xcb_get_geometry_reply_t *geometry)
3020{
3021 NETWinInfo i(kwinApp()->x11Connection(), w, kwinApp()->x11RootWindow(), NET::WMFrameExtents, NET::Properties2());
3022 NETStrut frame = i.frameExtents();
3023
3024 if (frame.left != 0 || frame.top != 0) {
3025 // left and top needed due to narrowing conversations restrictions in C++11
3026 const uint32_t left = frame.left;
3027 const uint32_t top = frame.top;
3028 const uint32_t values[] = {Xcb::toXNative(geometry->x - left), Xcb::toXNative(geometry->y - top)};
3029 xcb_configure_window(kwinApp()->x11Connection(), w, XCB_CONFIG_WINDOW_X | XCB_CONFIG_WINDOW_Y, values);
3030 }
3031}
3032
3033FocusChain *Workspace::focusChain() const
3034{
3035 return m_focusChain.get();
3036}
3037
3038ApplicationMenu *Workspace::applicationMenu() const
3039{
3040 return m_applicationMenu.get();
3041}
3042
3043Decoration::DecorationBridge *Workspace::decorationBridge() const
3044{
3045 return m_decorationBridge.get();
3046}
3047
3048Outline *Workspace::outline() const
3049{
3050 return m_outline.get();
3051}
3052
3053Placement *Workspace::placement() const
3054{
3055 return m_placement.get();
3056}
3057
3058RuleBook *Workspace::rulebook() const
3059{
3060 return m_rulebook.get();
3061}
3062
3063ScreenEdges *Workspace::screenEdges() const
3064{
3065 return m_screenEdges.get();
3066}
3067
3068TileManager *Workspace::tileManager(Output *output)
3069{
3070 return m_tileManagers.at(output).get();
3071}
3072
3073#if KWIN_BUILD_TABBOX
3074TabBox::TabBox *Workspace::tabbox() const
3075{
3076 return m_tabbox.get();
3077}
3078#endif
3079
3080#if KWIN_BUILD_ACTIVITIES
3081Activities *Workspace::activities() const
3082{
3083 return m_activities.get();
3084}
3085#endif
3086
3087} // namespace
3088
3089#include "moc_workspace.cpp"
void currentChanged(const QString &id)
static bool wasCrash()
Definition main.cpp:172
void x11ConnectionChanged()
void x11ConnectionAboutToBeDestroyed()
@ OperationModeX11
KWin uses only X11 for managing windows and compositing.
Definition main.h:87
void retrieveHelpers()
Definition atoms.cpp:77
Replacement for QCursor.
Definition cursor.h:102
QPointF pos()
Definition cursor.cpp:204
int themeSize() const
The size of the currently used Cursor theme.
Definition cursor.h:322
const QString & themeName() const
The name of the currently used Cursor theme.
Definition cursor.h:317
static Cursors * self()
Definition cursor.cpp:35
Cursor * mouse() const
Definition cursor.h:266
This class is a wrapper for the org.kde.KWin D-Bus interface.
QStringList loadedEffects
CompositingType compositingType
Q_SCRIPTABLE QString supportInformation(const QString &name) const
QStringList activeEffects
Singleton class to handle the various focus chains.
Definition focuschain.h:39
void remove(KWin::Window *window)
Removes window from all focus chains.
void setActiveWindow(KWin::Window *window)
Definition focuschain.h:219
void setSeparateScreenFocus(bool enabled)
Definition focuschain.h:214
Driver driver() const
Version glslVersion() const
QByteArrayView glVersionString() const
bool isVirtualMachine() const
QByteArrayView glVendorString() const
OpenGLPlatformInterface platformInterface() const
bool isGLES() const
Version glVersion() const
bool isLooseBinding() const
bool isMesaDriver() const
ChipClass chipClass() const
QByteArrayView glShadingLanguageVersionString() const
Version driverVersion() const
Version mesaVersion() const
QByteArrayView glRendererString() const
void lostLeader()
Definition group.cpp:109
const QList< X11Window * > & members() const
Definition group.h:68
void prependInputEventFilter(InputEventFilter *filter)
Definition input.cpp:2678
bool isPlaceable() const override
int focusStealingPreventionLevel
Definition options.h:137
int centerSnapZone
Definition options.h:125
void rollOverDesktopsChanged(bool enabled)
int borderSnapZone
Definition options.h:117
bool borderlessMaximizedWindows
Definition options.h:172
bool isHideUtilityWindowsForInactive() const
Definition options.h:619
int windowSnapZone
Definition options.h:121
int delayFocusInterval
Definition options.h:97
void updateSettings()
Definition options.cpp:675
void configChanged()
bool activeMouseScreen
Definition options.h:110
bool isRollOverDesktops() const
Definition options.h:385
bool isSnapOnlyWhenOverlapping() const
Definition options.h:377
bool focusPolicyIsReasonable
Definition options.h:113
bool isNextFocusPrefersMouse() const
Definition options.h:248
bool isSeparateScreenFocus() const
Definition options.h:318
void separateScreenFocusChanged(bool)
This class is used to show the outline of a given geometry.
Definition outline.h:38
std::shared_ptr< OutputChangeSet > constChangeSet(Output *output) const
void unref()
Definition output.cpp:350
QRectF geometryF() const
Definition output.cpp:465
QRect geometry
Definition output.h:134
void add(Window *window)
void remove(Window *window)
static RootInfo * create()
Definition netinfo.cpp:26
static void destroy()
Definition netinfo.cpp:122
Class for controlling screen edges.
Definition screenedge.h:222
static Scripting * create(QObject *parent)
void tabletModeChanged(bool tabletMode)
Menu shown for a Window.
Definition useractions.h:46
bool isMenuWindow(const Window *window) const
QString toString() const
Definition version.cpp:70
Manages the number of available virtual desktops, the layout of those and which virtual desktop is th...
void layoutChanged(int columns, int rows)
void setRootInfo(NETRootInfo *info)
void desktopAdded(KWin::VirtualDesktop *desktop)
void setConfig(KSharedConfig::Ptr config)
void currentChanging(KWin::VirtualDesktop *currentDesktop, QPointF offset)
void currentChanged(KWin::VirtualDesktop *previousDesktop, KWin::VirtualDesktop *newDesktop)
void setNavigationWrappingAround(bool enabled)
void desktopRemoved(KWin::VirtualDesktop *desktop)
void windowRemoved(KWin::Window *)
QList< Window * > windows() const
void windowAdded(KWin::Window *)
QRectF frameGeometry
Definition window.h:431
virtual QList< Window * > mainWindows() const
Definition window.cpp:2256
qreal width
Definition window.h:99
bool isShown() const
Definition window.cpp:4232
void fullScreenChanged()
bool wantsTabFocus() const
Definition window.cpp:697
void setOutput(Output *output)
Definition window.cpp:241
bool isOnCurrentActivity() const
Definition window.cpp:3097
bool isOnAllDesktops() const
Definition window.h:2032
virtual bool isUnmanaged() const
Definition window.cpp:307
QUuid internalId
Definition window.h:259
void sendToOutput(Output *output)
Definition window.cpp:3622
bool isOnCurrentDesktop() const
Definition window.cpp:848
void setDesktops(QList< VirtualDesktop * > desktops)
Definition window.cpp:726
virtual MaximizeMode requestedMaximizeMode() const
Definition window.cpp:3999
bool wantsInput
Definition window.h:409
bool isMinimized() const
Definition window.h:988
bool isModal() const
Definition window.cpp:2285
bool isDesktop() const
Definition window.h:1922
QList< KWin::VirtualDesktop * > desktops
Definition window.h:295
virtual bool isRequestedFullScreen() const
Definition window.cpp:3940
QRectF moveResizeGeometry() const
Definition window.cpp:3286
bool isOnDesktop(VirtualDesktop *desktop) const
Definition window.cpp:843
QRectF rect
Definition window.h:113
void setMoveResizeOutput(Output *output)
Definition window.cpp:3302
const WindowRules * rules() const
Definition window.h:1048
const QList< Window * > & transients() const
Definition window.h:2088
qreal height
Definition window.h:104
void minimizedChanged()
void checkWorkspacePosition(QRectF oldGeometry=QRectF(), const VirtualDesktop *oldDesktop=nullptr)
Definition window.cpp:3661
void setHiddenByShowDesktop(bool hidden)
Definition window.cpp:4262
bool isDock() const
Definition window.h:1927
virtual bool hasStrut() const
Definition window.cpp:1787
virtual bool belongsToDesktop() const
Definition window.cpp:623
virtual StrutRect strutRect(StrutArea area) const
Definition window.cpp:1764
KWin::Output * output
Definition window.h:111
void updateLayer()
Definition window.cpp:560
virtual MaximizeMode maximizeMode() const
Definition window.cpp:3987
virtual bool isPlaceable() const
Definition window.cpp:4049
bool isDeleted() const
Definition window.cpp:540
QPointF checkPosition(QPointF pos, bool init=false) const
void resetUpdateToolWindowsTimer()
Window * activeWindow() const
Definition workspace.h:767
bool applyOutputConfiguration(const OutputConfiguration &config, const QList< Output * > &outputOrder={})
void updateClientArea()
~Workspace() override
QRectF clientArea(clientAreaOption, const Output *output, const VirtualDesktop *desktop) const
void windowHidden(Window *)
void constrain(Window *below, Window *above)
X11Window * findClient(std::function< bool(const X11Window *)> func) const
Finds the first Client matching the condition expressed by passed in func.
void activateWindow(Window *window, bool force=false)
void currentDesktopChangingCancelled()
const QList< Window * > & stackingOrder() const
Definition workspace.h:788
QList< Output * > outputOrder() const
void workspaceInitialized()
void removeX11Window(X11Window *)
Group * findGroup(xcb_window_t leader) const
void windowRemoved(KWin::Window *)
Window * findDesktop(bool topmost, VirtualDesktop *desktop) const
Definition layers.cpp:253
Output * activeOutput() const
void setupWindowShortcutDone(bool)
void currentDesktopChanged(KWin::VirtualDesktop *previousDesktop, KWin::Window *)
void updateStackingOrder(bool propagate_new_windows=false)
Definition layers.cpp:93
void currentDesktopChanging(KWin::VirtualDesktop *currentDesktop, QPointF delta, KWin::Window *)
void raiseWindow(Window *window, bool nogroup=false)
Definition layers.cpp:354
void windowActivated(KWin::Window *)
void checkTransients(xcb_window_t w)
Window * topWindowOnDesktop(VirtualDesktop *desktop, Output *output=nullptr, bool unconstrained=false, bool only_normal=true) const
Definition layers.cpp:224
void windowAdded(KWin::Window *)
void setOutputOrder(const QList< Output * > &order)
bool showingDesktop() const
Definition workspace.h:804
QList< Output * > outputs() const
Definition workspace.h:762
void setActiveWindow(Window *window)
const QList< Window * > windows() const
Definition workspace.h:248
void removeUnmanaged(X11Window *)
void currentActivityChanged()
bool requestFocus(Window *window, bool force=false)
friend class StackingUpdatesBlocker
Definition workspace.h:704
void configChanged()
void cancelDelayFocus()
void setShowingDesktop(bool showing, bool animated=true)
QRect geometry() const
void windowMinimizedChanged(KWin::Window *)
Output * outputAt(const QPointF &pos) const
void removeDeleted(Window *)
void unconstrain(Window *below, Window *above)
Output * findOutput(Output *reference, Direction direction, bool wrapAround=false) const
void addDeleted(Window *)
void deletedRemoved(KWin::Window *)
void slotReconfigure()
X11EventFilterContainer(X11EventFilter *filter)
Definition workspace.cpp:80
X11EventFilter * filter() const
Definition workspace.cpp:85
xcb_window_t inputId() const
Definition x11window.h:82
static void deleteClient(X11Window *c)
Does 'delete c;'.
xcb_window_t frameId() const
static void cleanupX11()
bool isUnmanaged() const override
xcb_window_t window() const
void changeClientLeaderGroup(Group *gr)
xcb_window_t wmClientLeader() const
const Group * group() const override
Definition x11window.h:565
xcb_window_t wrapperId() const
static Extensions * self()
Definition xcbutils.cpp:346
static void destroy()
Definition xcbutils.cpp:354
void show(const QString &message, const QString &iconName, int timeout)
Definition osd.cpp:35
QList< KWayland::Client::Output * > outputs
RootInfo * rootInfo()
Definition netinfo.h:59
Gravity
Definition globals.h:150
Session::Type type
Definition session.cpp:17
clientAreaOption
Definition globals.h:48
@ FullScreenArea
Definition globals.h:53
@ MovementArea
Definition globals.h:50
@ MaximizeArea
Definition globals.h:51
@ MaximizeFullArea
Definition globals.h:52
@ ScreenArea
Definition globals.h:57
@ PlacementArea
Definition globals.h:49
@ FullArea
Definition globals.h:56
@ WorkArea
Definition globals.h:55
const QPoint invalidPoint(INT_MIN, INT_MIN)
@ MaximizeVertical
The window is maximized vertically.
Definition common.h:76
@ MaximizeRestore
The window is not maximized in any direction.
Definition common.h:75
@ MaximizeHorizontal
Definition common.h:77
@ MaximizeFull
Equal to MaximizeVertical | MaximizeHorizontal.
Definition common.h:79
@ StrutAreaRight
Definition common.h:38
@ StrutAreaBottom
Definition common.h:39
@ StrutAreaTop
Definition common.h:37
@ StrutAreaLeft
Definition common.h:40
@ StrutAreaAll
Definition common.h:41
WaylandServer * waylandServer()
Workspace * workspace()
Definition workspace.h:830
@ EglPlatformInterface
Definition globals.h:45
@ GlxPlatformInterface
Definition globals.h:44
QList< StrutRect > StrutRects
Definition common.h:60
@ QPainterCompositing
Definition globals.h:39
@ NoCompositing
Definition globals.h:29
@ OpenGLCompositing
Definition globals.h:37
Options * options
Definition main.cpp:73
InputRedirection * input()
Definition input.h:549
EffectsHandler * effects
KWIN_EXPORT Atoms * atoms
Definition main.cpp:74
Predicate
Defines Predicates on how to search for a Client.
Definition x11window.h:42
std::unique_ptr< T, CDeleter > UniqueCPtr
Definition c_ptr.h:28
#define SNAP_WINDOW_RIGHT
#define SNAP_WINDOW_BOTTOM
#define SNAP_BORDER_RIGHT
#define SNAP_BORDER_TOP
#define SNAP_WINDOW_C_RIGHT
#define SNAP_WINDOW_LEFT
#define SNAP_BORDER_LEFT
#define SNAP_WINDOW_C_LEFT
#define SNAP_WINDOW_C_TOP
#define SNAP_WINDOW_C_BOTTOM
#define SNAP_BORDER_BOTTOM
#define SNAP_WINDOW_TOP