KWin
Loading...
Searching...
No Matches
window.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: 2015 Martin Gräßlin <mgraesslin@kde.org>
6 SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
7
8 SPDX-License-Identifier: GPL-2.0-or-later
9*/
10#include "window.h"
11
12#if KWIN_BUILD_ACTIVITIES
13#include "activities.h"
14#endif
15#include "appmenu.h"
16#include "client_machine.h"
17#include "compositor.h"
18#include "core/output.h"
21#include "focuschain.h"
22#include "input.h"
23#include "outline.h"
24#include "placement.h"
25#include "scene/windowitem.h"
27#include "screenedge.h"
28#include "shadow.h"
29#if KWIN_BUILD_TABBOX
30#include "tabbox/tabbox.h"
31#endif
32#include "tiles/tilemanager.h"
33#include "useractions.h"
34#include "virtualdesktops.h"
35#include "wayland/output.h"
37#include "wayland/surface.h"
38#include "wayland_server.h"
39#include "workspace.h"
40
41#include <KDecoration2/DecoratedClient>
42#include <KDecoration2/Decoration>
43#include <KDesktopFile>
44
45#include <QDebug>
46#include <QDir>
47#include <QMouseEvent>
48#include <QStyleHints>
49
50namespace KWin
51{
52
53static inline int sign(int v)
54{
55 return (v > 0) - (v < 0);
56}
57
58QHash<QString, std::weak_ptr<Decoration::DecorationPalette>> Window::s_palettes;
59std::shared_ptr<Decoration::DecorationPalette> Window::s_defaultPalette;
60
62 : m_output(workspace()->activeOutput())
63 , ready_for_painting(false)
64 , m_internalId(QUuid::createUuid())
65 , m_clientMachine(new ClientMachine(this))
66 , m_skipCloseAnimation(false)
67 , m_colorScheme(QStringLiteral("kdeglobals"))
68 , m_moveResizeOutput(workspace()->activeOutput())
69{
71
74
75 connect(this, &Window::windowShown, this, &Window::hiddenChanged);
76 connect(this, &Window::windowHidden, this, &Window::hiddenChanged);
77
79
80 // If the user manually moved the window, don't restore it after the keyboard closes
81 connect(this, &Window::interactiveMoveResizeFinished, this, [this]() {
83 });
84 connect(this, &Window::maximizedChanged, this, [this]() {
86 });
87 connect(this, &Window::fullScreenChanged, this, [this]() {
89 });
90
91 // replace on-screen-display on size changes
92 connect(this, &Window::frameGeometryChanged, this, [this](const QRectF &old) {
93 if (isOnScreenDisplay() && !frameGeometry().isEmpty() && old.size() != frameGeometry().size() && isPlaceable()) {
94 GeometryUpdatesBlocker blocker(this);
95 workspace()->placement()->place(this, workspace()->clientArea(PlacementArea, this, workspace()->activeOutput()));
96 }
97 });
98
99 connect(Workspace::self()->applicationMenu(), &ApplicationMenu::applicationMenuEnabledChanged, this, [this] {
101 });
102 connect(&m_offscreenFramecallbackTimer, &QTimer::timeout, this, &Window::maybeSendFrameCallback);
103}
104
106{
107 if (m_tile) {
108 m_tile->removeWindow(this);
109 }
110 Q_ASSERT(m_blockGeometryUpdates == 0);
111}
112
114{
115 ++m_refCount;
116}
117
119{
120 --m_refCount;
121 if (m_refCount) {
122 return;
123 }
124 if (m_deleted) {
125 workspace()->removeDeleted(this);
126 }
127 delete this;
128}
129
130QDebug operator<<(QDebug debug, const Window *window)
131{
132 QDebugStateSaver saver(debug);
133 debug.nospace();
134 if (window) {
135 debug << window->metaObject()->className() << '(' << static_cast<const void *>(window);
136 if (const SurfaceInterface *surface = window->surface()) {
137 debug << ", surface=" << surface;
138 }
139 if (window->isClient()) {
140 if (!window->isPopupWindow()) {
141 debug << ", caption=" << window->caption();
142 }
143 if (window->transientFor()) {
144 debug << ", transientFor=" << window->transientFor();
145 }
146 }
147 if (debug.verbosity() > 2) {
148 debug << ", frameGeometry=" << window->frameGeometry();
149 debug << ", resourceName=" << window->resourceName();
150 debug << ", resourceClass=" << window->resourceClass();
151 }
152 debug << ')';
153 } else {
154 debug << "Window(0x0)";
155 }
156 return debug;
157}
158
160{
161 if (const WindowItem *item = windowItem()) {
162 return item->mapToGlobal(item->boundingRect());
163 }
164 return QRectF();
165}
166
171QString Window::wmClientMachine(bool use_localhost) const
172{
173 if (!m_clientMachine) {
174 // this should never happen
175 return QString();
176 }
177 if (use_localhost && m_clientMachine->isLocal()) {
178 // special name for the local machine (localhost)
180 }
181 return m_clientMachine->hostName();
182}
183
184void Window::setResourceClass(const QString &name, const QString &className)
185{
186 resource_name = name;
187 resource_class = className;
188 Q_EMIT windowClassChanged();
189}
190
191qreal Window::opacity() const
192{
193 return m_opacity;
194}
195
196void Window::setOpacity(qreal opacity)
197{
198 opacity = std::clamp(opacity, 0.0, 1.0);
199 if (m_opacity == opacity) {
200 return;
201 }
202 const qreal oldOpacity = m_opacity;
204 Q_EMIT opacityChanged(this, oldOpacity);
205}
206
208{
210 if (!scene) {
211 return false;
212 }
213
214 m_windowItem = createItem(scene);
215 m_windowItem->setParentItem(scene->containerItem());
216
219
220 return true;
221}
222
224{
225 m_windowItem.reset();
226}
227
229{
230 if (!ready_for_painting) {
231 ready_for_painting = true;
233 }
234}
235
237{
238 return m_output;
239}
240
242{
243 if (m_output != output) {
245 Q_EMIT outputChanged();
246 }
247}
248
250{
251 return isOnOutput(workspace()->activeOutput());
252}
253
254bool Window::isOnOutput(Output *output) const
255{
256 return output->geometry().intersects(frameGeometry().toRect());
257}
258
260{
261 return m_shadow.get();
262}
263
265{
266 if (m_shadow) {
267 if (!m_shadow->updateShadow()) {
268 m_shadow.reset();
269 }
270 Q_EMIT shadowChanged();
271 } else {
273 if (m_shadow) {
274 Q_EMIT shadowChanged();
275 }
276 }
277}
278
280{
281 return m_windowItem ? m_windowItem->effectWindow() : nullptr;
282}
283
285{
286 return m_windowItem ? m_windowItem->effectWindow() : nullptr;
287}
288
290{
291 if (m_windowItem) {
292 return m_windowItem->surfaceItem();
293 }
294 return nullptr;
295}
296
298{
299 return !isFullScreen() && maximizeMode() != MaximizeFull;
300}
301
303{
304 return false;
305}
306
308{
309 return false;
310}
311
312void Window::elevate(bool elevate)
313{
314 if (m_windowItem) {
315 if (elevate) {
316 m_windowItem->elevate();
317 } else {
318 m_windowItem->deelevate();
319 }
320 }
321}
322
323pid_t Window::pid() const
324{
325 return -1;
326}
327
329{
331}
332
334{
335 if (set == m_skipCloseAnimation) {
336 return;
337 }
340}
341
343{
344 return m_surface;
345}
346
348{
349 if (m_surface == surface) {
350 return;
351 }
353 Q_EMIT surfaceChanged();
354}
355
357{
358 return m_stackingOrder;
359}
360
362{
363 if (m_stackingOrder != order) {
364 m_stackingOrder = order;
365 Q_EMIT stackingOrderChanged();
366 }
367}
368
369QString Window::windowRole() const
370{
371 return QString();
372}
373
375{
376 QMatrix4x4 m;
377 m.translate(-m_bufferGeometry.x(), -m_bufferGeometry.y());
378 return m;
379}
380
381bool Window::hitTest(const QPointF &point) const
382{
383 if (isDecorated()) {
384 if (m_decoration.inputRegion.contains(flooredPoint(mapToFrame(point)))) {
385 return true;
386 }
387 }
388 if (m_surface && m_surface->isMapped()) {
389 return m_surface->inputSurfaceAt(mapToLocal(point));
390 }
391 return exclusiveContains(m_bufferGeometry, point);
392}
393
394QPointF Window::mapToFrame(const QPointF &point) const
395{
396 return point - frameGeometry().topLeft();
397}
398
399QPointF Window::mapToLocal(const QPointF &point) const
400{
401 return point - bufferGeometry().topLeft();
402}
403
404QPointF Window::mapFromLocal(const QPointF &point) const
405{
406 return point + bufferGeometry().topLeft();
407}
408
410{
411 if (!m_clientMachine) {
412 return true;
413 }
414 return m_clientMachine->isLocal();
415}
416
417QMargins Window::frameMargins() const
418{
419 return QMargins(borderLeft(), borderTop(), borderRight(), borderBottom());
420}
421
423{
424}
425
426bool Window::belongToSameApplication(const Window *c1, const Window *c2, SameApplicationChecks checks)
427{
428 return c1->belongsToSameApplication(c2, checks);
429}
430
431xcb_timestamp_t Window::userTime() const
432{
433 return XCB_TIME_CURRENT_TIME;
434}
435
437{
438 set = rules()->checkSkipSwitcher(set);
439 if (set == skipSwitcher()) {
440 return;
441 }
442 m_skipSwitcher = set;
445 Q_EMIT skipSwitcherChanged();
446}
447
449{
450 b = rules()->checkSkipPager(b);
451 if (b == skipPager()) {
452 return;
453 }
454 m_skipPager = b;
457 Q_EMIT skipPagerChanged();
458}
459
461{
462}
463
465{
466 int was_wants_tab_focus = wantsTabFocus();
467 if (b == skipTaskbar()) {
468 return;
469 }
470 m_skipTaskbar = b;
473 if (was_wants_tab_focus != wantsTabFocus()) {
475 }
476 Q_EMIT skipTaskbarChanged();
477}
478
484
488
492
493void Window::setIcon(const QIcon &icon)
494{
495 m_icon = icon;
496 Q_EMIT iconChanged();
497}
498
499void Window::setActive(bool act)
500{
501 if (isDeleted()) {
502 return;
503 }
504 if (m_active == act) {
505 return;
506 }
507 m_active = act;
508 const int ruledOpacity = m_active
509 ? rules()->checkOpacityActive(qRound(opacity() * 100.0))
510 : rules()->checkOpacityInactive(qRound(opacity() * 100.0));
511 setOpacity(ruledOpacity / 100.0);
512 workspace()->setActiveWindow(act ? this : nullptr);
513
514 if (!m_active) {
516 }
517
518 if (!m_active && shadeMode() == ShadeActivated) {
520 }
521
523 updateLayer(); // active windows may get different layer
524 auto mainwindows = mainWindows();
525 for (auto it = mainwindows.constBegin(); it != mainwindows.constEnd(); ++it) {
526 if ((*it)->isFullScreen()) { // fullscreens go high even if their transient is active
527 (*it)->updateLayer();
528 }
529 }
530
531 doSetActive();
532 Q_EMIT activeChanged();
534}
535
537{
538}
539
541{
542 return m_deleted;
543}
544
546{
547 Q_ASSERT(!m_deleted);
548 m_deleted = true;
549 workspace()->addDeleted(this);
550}
551
553{
554 if (m_layer == UnknownLayer) {
555 const_cast<Window *>(this)->m_layer = rules()->checkLayer(belongsToLayer());
556 }
557 return m_layer;
558}
559
561{
562 if (isDeleted()) {
563 return;
564 }
565 if (layer() == rules()->checkLayer(belongsToLayer())) {
566 return;
567 }
569 m_layer = UnknownLayer; // invalidate, will be updated when doing restacking
570 for (auto it = transients().constBegin(), end = transients().constEnd(); it != end; ++it) {
571 (*it)->updateLayer();
572 }
573}
574
576{
577 if (isUnmanaged() || isInternal()) {
578 return OverlayLayer;
579 }
580 if (isLockScreen() && !waylandServer()) {
581 return OverlayLayer;
582 }
583 if (isInputMethod()) {
584 return OverlayLayer;
585 }
586 if (isLockScreenOverlay() && waylandServer() && waylandServer()->isScreenLocked()) {
587 return OverlayLayer;
588 }
589 if (isDesktop()) {
590 return DesktopLayer;
591 }
592 if (isSplash()) { // no damn annoying splashscreens
593 return NormalLayer; // getting in the way of everything else
594 }
595 if (isDock() || isAppletPopup()) {
596 return AboveLayer;
597 }
598 if (isPopupWindow()) {
599 return PopupLayer;
600 }
601 if (isOnScreenDisplay()) {
603 }
604 if (isNotification()) {
605 return NotificationLayer;
606 }
609 }
610 if (keepBelow()) {
611 return BelowLayer;
612 }
613 if (isActiveFullScreen()) {
614 return ActiveLayer;
615 }
616 if (keepAbove()) {
617 return AboveLayer;
618 }
619
620 return NormalLayer;
621}
622
624{
625 return false;
626}
627
629{
630 b = rules()->checkKeepAbove(b);
631 if (b && !rules()->checkKeepBelow(false)) {
632 setKeepBelow(false);
633 }
634 if (b == keepAbove()) {
635 return;
636 }
637 m_keepAbove = b;
639 updateLayer();
641
643}
644
646{
647}
648
650{
651 b = rules()->checkKeepBelow(b);
652 if (b && !rules()->checkKeepAbove(false)) {
653 setKeepAbove(false);
654 }
655 if (b == keepBelow()) {
656 return;
657 }
658 m_keepBelow = b;
660 updateLayer();
662
664}
665
667{
668}
669
671{
672 delete m_autoRaiseTimer;
673 m_autoRaiseTimer = new QTimer(this);
674 connect(m_autoRaiseTimer, &QTimer::timeout, this, &Window::autoRaise);
675 m_autoRaiseTimer->setSingleShot(true);
677}
678
680{
681 delete m_autoRaiseTimer;
682 m_autoRaiseTimer = nullptr;
683}
684
686{
687 workspace()->raiseWindow(this);
689}
690
692{
693 // The last window in the unconstrained stacking order is the most recently raised one.
694 return workspace()->topWindowOnDesktop(VirtualDesktopManager::self()->currentDesktop(), nullptr, true, false) == this;
695}
696
698{
699 return (isNormalWindow() || isDialog() || isAppletPopup()) && wantsInput();
700}
701
703{
704 // TODO
706}
707
709{
710 if (isActive()) {
711 set = false;
712 }
713 if (m_demandsAttention == set) {
714 return;
715 }
716 m_demandsAttention = set;
718 workspace()->windowAttentionChanged(this, set);
720}
721
725
726void Window::setDesktops(QList<VirtualDesktop *> desktops)
727{
728 // on x11 we can have only one desktop at a time
729 if (kwinApp()->operationMode() == Application::OperationModeX11 && desktops.size() > 1) {
730 desktops = QList<VirtualDesktop *>({desktops.last()});
731 }
732
734 if (desktops == m_desktops) {
735 return;
736 }
737
739
741 if (m_desktops.isEmpty()) {
743 } else {
745 auto currentDesktops = windowManagementInterface()->plasmaVirtualDesktops();
746 for (auto desktop : std::as_const(m_desktops)) {
747 if (!currentDesktops.contains(desktop->id())) {
749 } else {
750 currentDesktops.removeOne(desktop->id());
751 }
752 }
753 for (const auto &desktopId : std::as_const(currentDesktops)) {
755 }
756 }
757 }
758
759 auto transients_stacking_order = workspace()->ensureStackingOrder(transients());
760 for (auto it = transients_stacking_order.constBegin(); it != transients_stacking_order.constEnd(); ++it) {
761 (*it)->setDesktops(desktops);
762 }
763
764 if (isModal()) // if a modal dialog is moved, move the mainwindow with it as otherwise
765 // the (just moved) modal dialog will confusingly return to the mainwindow with
766 // the next desktop change
767 {
768 const auto windows = mainWindows();
769 for (Window *other : windows) {
770 other->setDesktops(desktops);
771 }
772 }
773
774 doSetDesktop();
775
778
779 Q_EMIT desktopsChanged();
780}
781
783{
784}
785
787{
788 if (m_desktops.contains(virtualDesktop)) {
789 return;
790 }
791 auto desktops = m_desktops;
792 desktops.append(virtualDesktop);
794}
795
797{
798 QList<VirtualDesktop *> currentDesktops;
799 if (m_desktops.isEmpty()) {
800 currentDesktops = VirtualDesktopManager::self()->desktops();
801 } else {
802 currentDesktops = m_desktops;
803 }
804
805 if (!currentDesktops.contains(virtualDesktop)) {
806 return;
807 }
808 auto desktops = currentDesktops;
809 desktops.removeOne(virtualDesktop);
811}
812
814{
815 if (b == isOnAllDesktops()) {
816 return;
817 }
818 if (b) {
819 setDesktops({});
820 } else {
821 setDesktops({VirtualDesktopManager::self()->currentDesktop()});
822 }
823}
824
825QList<VirtualDesktop *> Window::desktops() const
826{
827 return m_desktops;
828}
829
830QStringList Window::desktopIds() const
831{
832 const auto desks = desktops();
833 QStringList ids;
834 ids.reserve(desks.count());
835 std::transform(desks.constBegin(), desks.constEnd(),
836 std::back_inserter(ids),
837 [](const VirtualDesktop *vd) {
838 return vd->id();
839 });
840 return ids;
841}
842
844{
845 return isOnAllDesktops() || desktops().contains(desktop);
846}
847
849{
850 return isOnDesktop(VirtualDesktopManager::self()->currentDesktop());
851}
852
854{
855 return m_shadeMode;
856}
857
859{
860 return false;
861}
862
863void Window::setShade(bool set)
864{
866}
867
869{
870 if (!isShadeable()) {
871 return;
872 }
873 if (mode == ShadeHover && isInteractiveMove()) {
874 return; // causes geometry breaks and is probably nasty
875 }
876 if (isSpecialWindow() || !isDecorated()) {
877 mode = ShadeNone;
878 }
879
880 mode = rules()->checkShade(mode);
881 if (m_shadeMode == mode) {
882 return;
883 }
884
885 const bool wasShade = isShade();
886 const ShadeMode previousShadeMode = shadeMode();
887 m_shadeMode = mode;
888
889 if (wasShade == isShade()) {
890 // Decoration may want to update after e.g. hover-shade changes
891 Q_EMIT shadeChanged();
892 return; // No real change in shaded state
893 }
894
895 Q_ASSERT(isDecorated());
896 GeometryUpdatesBlocker blocker(this);
897
898 doSetShade(previousShadeMode);
900
901 Q_EMIT shadeChanged();
902}
903
904void Window::doSetShade(ShadeMode previousShadeMode)
905{
906}
907
913
919
921{
922 if (!isShade()) {
923 return;
924 }
925 m_shadeHoverTimer = new QTimer(this);
926 connect(m_shadeHoverTimer, &QTimer::timeout, this, &Window::shadeHover);
927 m_shadeHoverTimer->setSingleShot(true);
929}
930
932{
934 m_shadeHoverTimer = new QTimer(this);
935 connect(m_shadeHoverTimer, &QTimer::timeout, this, &Window::shadeUnhover);
936 m_shadeHoverTimer->setSingleShot(true);
938 }
939}
940
942{
943 delete m_shadeHoverTimer;
944 m_shadeHoverTimer = nullptr;
945}
946
948{
949 // If the mode is ShadeHover or ShadeActive, cancel shade too.
951}
952
954{
955 // TODO: still needed, remove?
956 return Qt::TopEdge;
957}
958
960{
961 if (!isDecorated()) {
962 return false;
963 }
964 const auto sectionUnderMouse = decoration()->sectionUnderMouse();
965 if (sectionUnderMouse == Qt::TitleBarArea) {
966 return true;
967 }
968 // check other sections based on titlebarPosition
969 switch (titlebarPosition()) {
970 case Qt::TopEdge:
971 return (sectionUnderMouse == Qt::TopLeftSection || sectionUnderMouse == Qt::TopSection || sectionUnderMouse == Qt::TopRightSection);
972 case Qt::LeftEdge:
973 return (sectionUnderMouse == Qt::TopLeftSection || sectionUnderMouse == Qt::LeftSection || sectionUnderMouse == Qt::BottomLeftSection);
974 case Qt::RightEdge:
975 return (sectionUnderMouse == Qt::BottomRightSection || sectionUnderMouse == Qt::RightSection || sectionUnderMouse == Qt::TopRightSection);
976 case Qt::BottomEdge:
977 return (sectionUnderMouse == Qt::BottomLeftSection || sectionUnderMouse == Qt::BottomSection || sectionUnderMouse == Qt::BottomRightSection);
978 default:
979 // nothing
980 return false;
981 }
982}
983
985{
986 const bool effectiveSet = rules()->checkMinimize(set);
987 if (m_minimized == effectiveSet) {
988 return;
989 }
990
991 if (effectiveSet && !isMinimizable()) {
992 return;
993 }
994
995 m_minimized = effectiveSet;
996 doMinimize();
997
999 Q_EMIT minimizedChanged();
1000}
1001
1003{
1004}
1005
1007{
1008 ensurePalette();
1009 return m_palette->palette();
1010}
1011
1017
1019{
1020 return rules()->checkDecoColor(QString());
1021}
1022
1023QString Window::colorScheme() const
1024{
1025 return m_colorScheme;
1026}
1027
1028void Window::setColorScheme(const QString &colorScheme)
1029{
1030 QString requestedColorScheme = colorScheme;
1031 if (requestedColorScheme.isEmpty()) {
1032 requestedColorScheme = QStringLiteral("kdeglobals");
1033 }
1034
1035 if (m_colorScheme == requestedColorScheme) {
1036 return;
1037 }
1038
1039 m_colorScheme = requestedColorScheme;
1040
1041 if (m_palette) {
1043 m_palette.reset();
1044
1045 // If there already was a palette, re-create it right away
1046 // so the signals for repainting the decoration are emitted.
1047 ensurePalette();
1048 }
1049
1050 Q_EMIT colorSchemeChanged();
1051}
1052
1057
1059{
1060 if (m_palette) {
1061 return;
1062 }
1063
1064 auto it = s_palettes.find(m_colorScheme);
1065
1066 if (it == s_palettes.end() || it->expired()) {
1067 m_palette = std::make_shared<Decoration::DecorationPalette>(m_colorScheme);
1068 if (m_palette->isValid()) {
1070 } else {
1071 if (!s_defaultPalette) {
1072 s_defaultPalette = std::make_shared<Decoration::DecorationPalette>(QStringLiteral("kdeglobals"));
1073 s_palettes[QStringLiteral("kdeglobals")] = s_defaultPalette;
1074 }
1075
1077 }
1079 if (m_colorScheme == QStringLiteral("kdeglobals")) {
1081 }
1082 } else {
1083 m_palette = it->lock();
1084 }
1085
1087
1089}
1090
1092{
1093 Q_EMIT paletteChanged(palette());
1094}
1095
1096QRectF Window::keepInArea(QRectF geometry, QRectF area, bool partial)
1097{
1098 if (partial) {
1099 // increase the area so that can have only 100 pixels in the area
1100 const QRectF geometry = moveResizeGeometry();
1101 area.setLeft(std::min(area.left() - geometry.width() + 100, area.left()));
1102 area.setTop(std::min(area.top() - geometry.height() + 100, area.top()));
1103 area.setRight(std::max(area.right() + geometry.width() - 100, area.right()));
1104 area.setBottom(std::max(area.bottom() + geometry.height() - 100, area.bottom()));
1105 }
1106 if (!partial) {
1107 // resize to fit into area
1108 if (area.width() < geometry.width() || area.height() < geometry.height()) {
1109 geometry = resizeWithChecks(geometry, geometry.size().boundedTo(area.size()));
1110 }
1111 }
1112
1113 if (geometry.right() > area.right() && geometry.width() <= area.width()) {
1114 geometry.moveRight(area.right());
1115 }
1116 if (geometry.bottom() > area.bottom() && geometry.height() <= area.height()) {
1117 geometry.moveBottom(area.bottom());
1118 }
1119
1120 if (geometry.left() < area.left()) {
1121 geometry.moveLeft(area.left());
1122 }
1123 if (geometry.top() < area.top()) {
1124 geometry.moveTop(area.top());
1125 }
1126 return geometry;
1127}
1128
1129void Window::keepInArea(QRectF area, bool partial)
1130{
1131 moveResize(keepInArea(moveResizeGeometry(), area, partial));
1132}
1133
1137QSizeF Window::maxSize() const
1138{
1139 return rules()->checkMaxSize(QSize(INT_MAX, INT_MAX));
1140}
1141
1145QSizeF Window::minSize() const
1146{
1147 return rules()->checkMinSize(QSize(0, 0));
1148}
1149
1166
1168{
1169 qCWarning(KWIN_CORE, "%s doesn't support setting maximized state", metaObject()->className());
1170}
1171
1172void Window::setMaximize(bool vertically, bool horizontally)
1173{
1175 if (vertically) {
1176 mode = MaximizeMode(mode | MaximizeVertical);
1177 }
1178 if (horizontally) {
1179 mode = MaximizeMode(mode | MaximizeHorizontal);
1180 }
1181
1182 maximize(mode);
1183}
1184
1186{
1187 Q_ASSERT(!isInteractiveMoveResize());
1188 Q_ASSERT(QWidget::keyboardGrabber() == nullptr);
1189 Q_ASSERT(QWidget::mouseGrabber() == nullptr);
1191 if (QApplication::activePopupWidget() != nullptr) {
1192 return false; // popups have grab
1193 }
1194 if (isRequestedFullScreen() && (workspace()->outputs().count() < 2 || !isMovableAcrossScreens())) {
1195 return false;
1196 }
1198 return false;
1199 }
1200
1202
1205
1206 m_interactiveMoveResize.initialGeometry = moveResizeGeometry();
1208 m_interactiveMoveResize.initialMaximizeMode = requestedMaximizeMode();
1209 m_interactiveMoveResize.initialQuickTileMode = quickTileMode();
1210 m_interactiveMoveResize.initialGeometryRestore = geometryRestore();
1211
1213 switch (interactiveMoveResizeGravity()) {
1214 case Gravity::Left:
1215 case Gravity::Right:
1216 // Quit maximized horizontally state if the window is resized horizontally.
1218 QRectF originalGeometry = geometryRestore();
1219 originalGeometry.setX(moveResizeGeometry().x());
1220 originalGeometry.setWidth(moveResizeGeometry().width());
1221 setGeometryRestore(originalGeometry);
1223 }
1224 break;
1225 case Gravity::Top:
1226 case Gravity::Bottom:
1227 // Quit maximized vertically state if the window is resized vertically.
1229 QRectF originalGeometry = geometryRestore();
1230 originalGeometry.setY(moveResizeGeometry().y());
1231 originalGeometry.setHeight(moveResizeGeometry().height());
1232 setGeometryRestore(originalGeometry);
1234 }
1235 break;
1236 case Gravity::TopLeft:
1238 case Gravity::TopRight:
1240 // Quit the maximized mode if the window is resized by dragging one of its corners.
1243 break;
1244 default:
1245 break;
1246 }
1247 }
1248
1249 if (m_tile && !m_tile->supportsResizeGravity(interactiveMoveResizeGravity())) {
1251 }
1252
1256 if (workspace()->screenEdges()->isDesktopSwitchingMovingClients()) {
1257 workspace()->screenEdges()->reserveDesktopSwitching(true, Qt::Vertical | Qt::Horizontal);
1258 }
1259 return true;
1260}
1261
1263{
1264 const bool wasMove = isInteractiveMove();
1265 GeometryUpdatesBlocker blocker(this);
1267
1269
1270 if (cancel) {
1272 if (m_interactiveMoveResize.initialMaximizeMode != MaximizeMode::MaximizeRestore) {
1274 setGeometryRestore(m_interactiveMoveResize.initialGeometryRestore);
1275 } else if (m_interactiveMoveResize.initialQuickTileMode) {
1276 setQuickTileMode(m_interactiveMoveResize.initialQuickTileMode, true);
1277 setGeometryRestore(m_interactiveMoveResize.initialGeometryRestore);
1278 }
1280 workspace()->sendWindowToOutput(this, moveResizeOutput()); // checks rule validity
1283 }
1284 }
1285
1289 } else if (wasMove && (input()->keyboardModifiers() & Qt::ShiftModifier)) {
1291 }
1293 workspace()->outline()->hide();
1294
1295 m_interactiveMoveResize.counter++;
1297}
1298
1299// This function checks if it actually makes sense to perform a restricted move/resize.
1300// If e.g. the titlebar is already outside of the workarea, there's no point in performing
1301// a restricted move resize, because then e.g. resize would also move the window (#74555).
1302// NOTE: Most of it is duplicated from handleMoveResize().
1304{
1306 return;
1307 }
1308 const QRectF &moveResizeGeom = moveResizeGeometry();
1309 QRectF desktopArea = workspace()->clientArea(WorkArea, this, moveResizeGeom.center());
1310 int left_marge, right_marge, top_marge, bottom_marge, titlebar_marge;
1311 // restricted move/resize - keep at least part of the titlebar always visible
1312 // how much must remain visible when moved away in that direction
1313 left_marge = std::min(100. + borderRight(), moveResizeGeom.width());
1314 right_marge = std::min(100. + borderLeft(), moveResizeGeom.width());
1315 // width/height change with opaque resizing, use the initial ones
1316 titlebar_marge = initialInteractiveMoveResizeGeometry().height();
1317 top_marge = borderBottom();
1318 bottom_marge = borderTop();
1319 if (isInteractiveResize()) {
1320 if (moveResizeGeom.bottom() < desktopArea.top() + top_marge) {
1322 }
1323 if (moveResizeGeom.top() > desktopArea.bottom() - bottom_marge) {
1325 }
1326 if (moveResizeGeom.right() < desktopArea.left() + left_marge) {
1328 }
1329 if (moveResizeGeom.left() > desktopArea.right() - right_marge) {
1331 }
1332 if (!isUnrestrictedInteractiveMoveResize() && moveResizeGeom.top() < desktopArea.top()) { // titlebar mustn't go out
1334 }
1335 }
1336 if (isInteractiveMove()) {
1337 if (moveResizeGeom.bottom() < desktopArea.top() + titlebar_marge) {
1339 }
1340 // no need to check top_marge, titlebar_marge already handles it
1341 if (moveResizeGeom.top() > desktopArea.bottom() - bottom_marge) { // titlebar mustn't go out
1343 }
1344 if (moveResizeGeom.right() < desktopArea.left() + left_marge) {
1346 }
1347 if (moveResizeGeom.left() > desktopArea.right() - right_marge) {
1349 }
1350 }
1351}
1352
1353// When the user pressed mouse on the titlebar, don't activate move immediately,
1354// since it may be just a click. Activate instead after a delay. Move used to be
1355// activated only after moving by several pixels, but that looks bad.
1357{
1358 Q_ASSERT(!m_interactiveMoveResize.delayedTimer);
1359 m_interactiveMoveResize.delayedTimer = new QTimer(this);
1360 m_interactiveMoveResize.delayedTimer->setSingleShot(true);
1361 connect(m_interactiveMoveResize.delayedTimer, &QTimer::timeout, this, [this]() {
1362 Q_ASSERT(isInteractiveMoveResizePointerButtonDown());
1363 if (!startInteractiveMoveResize()) {
1364 setInteractiveMoveResizePointerButtonDown(false);
1365 }
1366 updateCursor();
1368 });
1369 m_interactiveMoveResize.delayedTimer->start(QApplication::startDragTime());
1370}
1371
1372void Window::stopDelayedInteractiveMoveResize()
1373{
1374 delete m_interactiveMoveResize.delayedTimer;
1375 m_interactiveMoveResize.delayedTimer = nullptr;
1376}
1377
1378void Window::updateInteractiveMoveResize(const QPointF &currentGlobalCursor)
1379{
1380 handleInteractiveMoveResize(pos(), currentGlobalCursor);
1381}
1382
1383void Window::handleInteractiveMoveResize(const QPointF &local, const QPointF &global)
1384{
1385 const QRectF oldGeo = moveResizeGeometry();
1386 handleInteractiveMoveResize(local.x(), local.y(), global.x(), global.y());
1387 if (!isRequestedFullScreen() && isInteractiveMove()) {
1388 if (quickTileMode() != QuickTileMode(QuickTileFlag::None) && oldGeo != moveResizeGeometry()) {
1389 GeometryUpdatesBlocker blocker(this);
1390 setQuickTileMode(QuickTileFlag::None);
1391 const QRectF &geom_restore = geometryRestore();
1392 setInteractiveMoveOffset(QPointF(double(interactiveMoveOffset().x()) / double(oldGeo.width()) * double(geom_restore.width()),
1393 double(interactiveMoveOffset().y()) / double(oldGeo.height()) * double(geom_restore.height())));
1394 if (rules()->checkMaximize(MaximizeRestore) == MaximizeRestore) {
1395 setMoveResizeGeometry(geom_restore);
1396 }
1397 handleInteractiveMoveResize(local.x(), local.y(), global.x(), global.y()); // fix position
1398 }
1399
1400 if (input()->keyboardModifiers() & Qt::ShiftModifier) {
1401 resetQuickTilingMaximizationZones();
1402 const auto &r = quickTileGeometry(QuickTileFlag::Custom, global);
1403 if (r.isEmpty()) {
1404 workspace()->outline()->hide();
1405 } else {
1406 if (!workspace()->outline()->isActive() || workspace()->outline()->geometry() != r.toRect()) {
1407 workspace()->outline()->show(r.toRect(), moveResizeGeometry().toRect());
1408 }
1409 }
1410 } else {
1411 if (quickTileMode() == QuickTileMode(QuickTileFlag::None) && isResizable()) {
1412 checkQuickTilingMaximizationZones(global.x(), global.y());
1413 }
1414 if (!m_electricMaximizing) {
1415 // Only if we are in an electric maximizing gesture we should keep the outline,
1416 // otherwise we must make sure it's hidden
1417 workspace()->outline()->hide();
1418 }
1419 }
1420 }
1421}
1422
1423void Window::handleInteractiveMoveResize(qreal x, qreal y, qreal x_root, qreal y_root)
1424{
1425 if (isWaitingForInteractiveMoveResizeSync()) {
1426 return; // we're still waiting for the client or the timeout
1427 }
1428
1429 const Gravity gravity = interactiveMoveResizeGravity();
1430 if ((gravity == Gravity::None && !isMovableAcrossScreens())
1431 || (gravity != Gravity::None && (isShade() || !isResizable()))) {
1432 return;
1433 }
1434
1435 if (!isInteractiveMoveResize()) {
1436 QPointF p(QPointF(x /* - padding_left*/, y /* - padding_top*/) - interactiveMoveOffset());
1437 if (p.manhattanLength() >= QApplication::startDragDistance()) {
1438 if (!startInteractiveMoveResize()) {
1439 setInteractiveMoveResizePointerButtonDown(false);
1440 updateCursor();
1441 return;
1442 }
1443 updateCursor();
1444 } else {
1445 return;
1446 }
1447 }
1448
1449 // ShadeHover or ShadeActive, ShadeNormal was already avoided above
1450 if (gravity != Gravity::None && shadeMode() != ShadeNone) {
1451 setShade(ShadeNone);
1452 }
1453
1454 QPointF globalPos(x_root, y_root);
1455 // these two points limit the geometry rectangle, i.e. if bottomleft resizing is done,
1456 // the bottomleft corner should be at is at (topleft.x(), bottomright().y())
1457 QPointF topleft = globalPos - interactiveMoveOffset();
1458 QPointF bottomright = globalPos + invertedInteractiveMoveOffset();
1459 const QRectF currentMoveResizeGeom = moveResizeGeometry();
1460 QRectF nextMoveResizeGeom = moveResizeGeometry();
1461
1462 // TODO move whole group when moving its leader or when the leader is not mapped?
1463
1464 auto titleBarRect = [this](const QRectF &rect, bool &transposed, int &requiredPixels) -> QRectF {
1465 QRectF titleRect = rect;
1466 titleRect.moveTopLeft(QPointF(0, 0));
1467 switch (titlebarPosition()) {
1468 default:
1469 case Qt::TopEdge:
1470 titleRect.setHeight(borderTop());
1471 break;
1472 case Qt::LeftEdge:
1473 titleRect.setWidth(borderLeft());
1474 transposed = true;
1475 break;
1476 case Qt::BottomEdge:
1477 titleRect.setTop(titleRect.bottom() - borderBottom());
1478 break;
1479 case Qt::RightEdge:
1480 titleRect.setLeft(titleRect.right() - borderRight());
1481 transposed = true;
1482 break;
1483 }
1484 // When doing a restricted move we must always keep 100px of the titlebar
1485 // visible to allow the user to be able to move it again.
1486 requiredPixels = std::min(100 * (transposed ? titleRect.width() : titleRect.height()),
1487 rect.width() * rect.height());
1488 return titleRect;
1489 };
1490
1491 if (isInteractiveResize()) {
1492 if (m_tile && m_tile->supportsResizeGravity(gravity)) {
1493 m_tile->resizeFromGravity(gravity, x_root, y_root);
1494 return;
1495 }
1496
1497 QRectF orig = initialInteractiveMoveResizeGeometry();
1498 SizeMode sizeMode = SizeModeAny;
1499 auto calculateMoveResizeGeom = [&topleft, &bottomright, &orig, &nextMoveResizeGeom, &sizeMode, &gravity]() {
1500 switch (gravity) {
1501 case Gravity::TopLeft:
1502 nextMoveResizeGeom = QRectF(topleft, orig.bottomRight());
1503 break;
1504 case Gravity::BottomRight:
1505 nextMoveResizeGeom = QRectF(orig.topLeft(), bottomright);
1506 break;
1507 case Gravity::BottomLeft:
1508 nextMoveResizeGeom = QRectF(QPointF(topleft.x(), orig.y()), QPointF(orig.right(), bottomright.y()));
1509 break;
1510 case Gravity::TopRight:
1511 nextMoveResizeGeom = QRectF(QPointF(orig.x(), topleft.y()), QPointF(bottomright.x(), orig.bottom()));
1512 break;
1513 case Gravity::Top:
1514 nextMoveResizeGeom = QRectF(QPointF(orig.left(), topleft.y()), orig.bottomRight());
1515 sizeMode = SizeModeFixedH; // try not to affect height
1516 break;
1517 case Gravity::Bottom:
1518 nextMoveResizeGeom = QRectF(orig.topLeft(), QPointF(orig.right(), bottomright.y()));
1519 sizeMode = SizeModeFixedH;
1520 break;
1521 case Gravity::Left:
1522 nextMoveResizeGeom = QRectF(QPointF(topleft.x(), orig.top()), orig.bottomRight());
1523 sizeMode = SizeModeFixedW;
1524 break;
1525 case Gravity::Right:
1526 nextMoveResizeGeom = QRectF(orig.topLeft(), QPointF(bottomright.x(), orig.bottom()));
1527 sizeMode = SizeModeFixedW;
1528 break;
1529 case Gravity::None:
1530 Q_UNREACHABLE();
1531 break;
1532 }
1533 };
1534
1535 // first resize (without checking constrains), then snap, then check bounds, then check constrains
1536 calculateMoveResizeGeom();
1537 // adjust new size to snap to other windows/borders
1538 nextMoveResizeGeom = workspace()->adjustWindowSize(this, nextMoveResizeGeom, gravity);
1539
1540 if (!isUnrestrictedInteractiveMoveResize()) {
1541 // Make sure the titlebar isn't behind a restricted area. We don't need to restrict
1542 // the other directions. If not visible enough, move the window to the closest valid
1543 // point. We bruteforce this by slowly moving the window back to its previous position
1544 const StrutRects strut = workspace()->restrictedMoveArea(VirtualDesktopManager::self()->currentDesktop());
1545 QRegion availableArea(workspace()->clientArea(FullArea, this, workspace()->activeOutput()).toRect());
1546 for (const QRect &rect : strut) {
1547 availableArea -= rect;
1548 }
1549 bool transposed = false;
1550 int requiredPixels;
1551 QRectF bTitleRect = titleBarRect(nextMoveResizeGeom, transposed, requiredPixels);
1552 int lastVisiblePixels = -1;
1553 QRectF lastTry = nextMoveResizeGeom;
1554 bool titleFailed = false;
1555 for (;;) {
1556 const QRect titleRect = bTitleRect.translated(nextMoveResizeGeom.topLeft()).toRect();
1557 int visiblePixels = 0;
1558 int realVisiblePixels = 0;
1559 for (const QRect &rect : availableArea) {
1560 const QRect r = rect & titleRect;
1561 realVisiblePixels += r.width() * r.height();
1562 if ((transposed && r.width() == titleRect.width()) || // Only the full size regions...
1563 (!transposed && r.height() == titleRect.height())) { // ...prevents long slim areas
1564 visiblePixels += r.width() * r.height();
1565 }
1566 }
1567
1568 if (visiblePixels >= requiredPixels) {
1569 break; // We have reached a valid position
1570 }
1571
1572 if (realVisiblePixels <= lastVisiblePixels) {
1573 if (titleFailed && realVisiblePixels < lastVisiblePixels) {
1574 break; // we won't become better
1575 } else {
1576 if (!titleFailed) {
1577 nextMoveResizeGeom = lastTry;
1578 }
1579 titleFailed = true;
1580 }
1581 }
1582 lastVisiblePixels = realVisiblePixels;
1583 QRectF currentTry = nextMoveResizeGeom;
1584 lastTry = currentTry;
1585
1586 // Not visible enough, move the window to the closest valid point. We bruteforce
1587 // this by slowly moving the window back to its previous position.
1588 // The geometry changes at up to two edges, the one with the title (if) shall take
1589 // precedence. The opposing edge has no impact on visiblePixels and only one of
1590 // the adjacent can alter at a time, ie. it's enough to ignore adjacent edges
1591 // if the title edge altered
1592 bool leftChanged = !qFuzzyCompare(currentMoveResizeGeom.left(), currentTry.left());
1593 bool rightChanged = !qFuzzyCompare(currentMoveResizeGeom.right(), currentTry.right());
1594 bool topChanged = !qFuzzyCompare(currentMoveResizeGeom.top(), currentTry.top());
1595 bool btmChanged = !qFuzzyCompare(currentMoveResizeGeom.bottom(), currentTry.bottom());
1596 auto fixChangedState = [titleFailed](bool &major, bool &counter, bool &ad1, bool &ad2) {
1597 counter = false;
1598 if (titleFailed) {
1599 major = false;
1600 }
1601 if (major) {
1602 ad1 = ad2 = false;
1603 }
1604 };
1605 switch (titlebarPosition()) {
1606 default:
1607 case Qt::TopEdge:
1608 fixChangedState(topChanged, btmChanged, leftChanged, rightChanged);
1609 break;
1610 case Qt::LeftEdge:
1611 fixChangedState(leftChanged, rightChanged, topChanged, btmChanged);
1612 break;
1613 case Qt::BottomEdge:
1614 fixChangedState(btmChanged, topChanged, leftChanged, rightChanged);
1615 break;
1616 case Qt::RightEdge:
1617 fixChangedState(rightChanged, leftChanged, topChanged, btmChanged);
1618 break;
1619 }
1620 if (topChanged) {
1621 currentTry.setTop(currentTry.y() + qBound(-1.0, currentMoveResizeGeom.y() - currentTry.y(), 1.0));
1622 } else if (leftChanged) {
1623 currentTry.setLeft(currentTry.x() + qBound(-1.0, currentMoveResizeGeom.x() - currentTry.x(), 1.0));
1624 } else if (btmChanged) {
1625 currentTry.setBottom(currentTry.bottom() + qBound(-1.0, currentMoveResizeGeom.bottom() - currentTry.bottom(), 1.0));
1626 } else if (rightChanged) {
1627 currentTry.setRight(currentTry.right() + qBound(-1.0, currentMoveResizeGeom.right() - currentTry.right(), 1.0));
1628 } else {
1629 break; // no position changed - that's certainly not good
1630 }
1631 nextMoveResizeGeom = currentTry;
1632 }
1633 }
1634
1635 // Always obey size hints, even when in "unrestricted" mode
1636 QSizeF size = constrainFrameSize(nextMoveResizeGeom.size(), sizeMode);
1637 // the new topleft and bottomright corners (after checking size constrains), if they'll be needed
1638 topleft = QPointF(nextMoveResizeGeom.right() - size.width(), nextMoveResizeGeom.bottom() - size.height());
1639 bottomright = QPointF(nextMoveResizeGeom.left() + size.width(), nextMoveResizeGeom.top() + size.height());
1640 orig = nextMoveResizeGeom;
1641
1642 // if aspect ratios are specified, both dimensions may change.
1643 // Therefore grow to the right/bottom if needed.
1644 // TODO it should probably obey gravity rather than always using right/bottom ?
1645 if (sizeMode == SizeModeFixedH) {
1646 orig.setRight(bottomright.x());
1647 } else if (sizeMode == SizeModeFixedW) {
1648 orig.setBottom(bottomright.y());
1649 }
1650
1651 calculateMoveResizeGeom();
1652 } else if (isInteractiveMove()) {
1653 Q_ASSERT(gravity == Gravity::None);
1654 if (!isMovable()) { // isMovableAcrossScreens() must have been true to get here
1655 // Special moving of maximized windows on Xinerama screens
1656 Output *output = workspace()->outputAt(globalPos);
1657 if (isRequestedFullScreen()) {
1658 nextMoveResizeGeom = workspace()->clientArea(FullScreenArea, this, output);
1659 } else {
1660 nextMoveResizeGeom = workspace()->clientArea(MaximizeArea, this, output);
1661 const QSizeF adjSize = constrainFrameSize(nextMoveResizeGeom.size(), SizeModeMax);
1662 if (adjSize != nextMoveResizeGeom.size()) {
1663 QRectF r(nextMoveResizeGeom);
1664 nextMoveResizeGeom.setSize(adjSize);
1665 nextMoveResizeGeom.moveCenter(r.center());
1666 }
1667 }
1668 } else {
1669 // first move, then snap, then check bounds
1670 QRectF geometry = nextMoveResizeGeom;
1671 geometry.moveTopLeft(topleft);
1672 geometry.moveTopLeft(workspace()->adjustWindowPosition(this, geometry.topLeft(),
1673 isUnrestrictedInteractiveMoveResize()));
1674 nextMoveResizeGeom = geometry;
1675
1676 if (!isUnrestrictedInteractiveMoveResize()) {
1677 const StrutRects strut = workspace()->restrictedMoveArea(VirtualDesktopManager::self()->currentDesktop());
1678 QRegion availableArea(workspace()->clientArea(FullArea, this, workspace()->activeOutput()).toRect());
1679 for (const QRect &rect : strut) {
1680 availableArea -= rect; // Strut areas
1681 }
1682 bool transposed = false;
1683 int requiredPixels;
1684 QRectF bTitleRect = titleBarRect(nextMoveResizeGeom, transposed, requiredPixels);
1685 for (;;) {
1686 QRectF currentTry = nextMoveResizeGeom;
1687 const QRect titleRect = bTitleRect.translated(currentTry.topLeft()).toRect();
1688 int visiblePixels = 0;
1689 for (const QRect &rect : availableArea) {
1690 const QRect r = rect & titleRect;
1691 if ((transposed && r.width() == titleRect.width()) || // Only the full size regions...
1692 (!transposed && r.height() == titleRect.height())) { // ...prevents long slim areas
1693 visiblePixels += r.width() * r.height();
1694 }
1695 }
1696 if (visiblePixels >= requiredPixels) {
1697 break; // We have reached a valid position
1698 }
1699
1700 // (esp.) if there're more screens with different struts (panels) it the titlebar
1701 // will be movable outside the movearea (covering one of the panels) until it
1702 // crosses the panel "too much" (not enough visiblePixels) and then stucks because
1703 // it's usually only pushed by 1px to either direction
1704 // so we first check whether we intersect suc strut and move the window below it
1705 // immediately (it's still possible to hit the visiblePixels >= titlebarArea break
1706 // by moving the window slightly downwards, but it won't stuck)
1707 // see bug #274466
1708 // and bug #301805 for why we can't just match the titlearea against the screen
1709 if (workspace()->outputs().count() > 1) { // optimization
1710 // TODO: could be useful on partial screen struts (half-width panels etc.)
1711 int newTitleTop = -1;
1712 for (const QRect &region : strut) {
1713 QRectF r = region;
1714 if (r.top() == 0 && r.width() > r.height() && // "top panel"
1715 r.intersects(currentTry) && currentTry.top() < r.bottom()) {
1716 newTitleTop = r.bottom();
1717 break;
1718 }
1719 }
1720 if (newTitleTop > -1) {
1721 currentTry.moveTop(newTitleTop); // invalid position, possibly on screen change
1722 nextMoveResizeGeom = currentTry;
1723 break;
1724 }
1725 }
1726
1727 int dx = sign(currentMoveResizeGeom.x() - currentTry.x()),
1728 dy = sign(currentMoveResizeGeom.y() - currentTry.y());
1729 if (visiblePixels && dx) { // means there's no full width cap -> favor horizontally
1730 dy = 0;
1731 } else if (dy) {
1732 dx = 0;
1733 }
1734
1735 // Move it back
1736 currentTry.translate(dx, dy);
1737 nextMoveResizeGeom = currentTry;
1738
1739 // sinces nextMoveResizeGeom is fractional, at best it is within 1 unit of currentMoveResizeGeom
1740 if (std::abs(currentMoveResizeGeom.left() - nextMoveResizeGeom.left()) <= 1.0
1741 && std::abs(currentMoveResizeGeom.right() - nextMoveResizeGeom.right()) <= 1.0
1742 && std::abs(currentMoveResizeGeom.top() - nextMoveResizeGeom.top()) <= 1.0
1743 && std::abs(currentMoveResizeGeom.bottom() - nextMoveResizeGeom.bottom()) <= 1.0) {
1744 break; // Prevent lockup
1745 }
1746 }
1747 }
1748 }
1749 } else {
1750 Q_UNREACHABLE();
1751 }
1752
1753 if (nextMoveResizeGeom != currentMoveResizeGeom) {
1754 if (isInteractiveMove()) {
1755 move(nextMoveResizeGeom.topLeft());
1756 } else {
1757 doInteractiveResizeSync(nextMoveResizeGeom);
1758 }
1759
1760 Q_EMIT interactiveMoveResizeStepped(nextMoveResizeGeom);
1761 }
1762}
1763
1764StrutRect Window::strutRect(StrutArea area) const
1765{
1766 return StrutRect();
1767}
1768
1769StrutRects Window::strutRects() const
1770{
1771 StrutRects region;
1772 if (const StrutRect strut = strutRect(StrutAreaTop); strut.isValid()) {
1773 region += strut;
1774 }
1775 if (const StrutRect strut = strutRect(StrutAreaRight); strut.isValid()) {
1776 region += strut;
1777 }
1778 if (const StrutRect strut = strutRect(StrutAreaBottom); strut.isValid()) {
1779 region += strut;
1780 }
1781 if (const StrutRect strut = strutRect(StrutAreaLeft); strut.isValid()) {
1782 region += strut;
1783 }
1784 return region;
1785}
1786
1787bool Window::hasStrut() const
1788{
1789 return false;
1790}
1791
1792void Window::setupWindowManagementInterface()
1793{
1794 if (m_windowManagementInterface) {
1795 // already setup
1796 return;
1797 }
1798 if (!waylandServer() || !waylandServer()->windowManagement()) {
1799 return;
1800 }
1801 auto w = waylandServer()->windowManagement()->createWindow(this, internalId());
1802 w->setTitle(caption());
1803 w->setActive(isActive());
1804 w->setFullscreen(isFullScreen());
1805 w->setKeepAbove(keepAbove());
1806 w->setKeepBelow(keepBelow());
1807 w->setMaximized(maximizeMode() == KWin::MaximizeFull);
1808 w->setMinimized(isMinimized());
1809 w->setDemandsAttention(isDemandingAttention());
1810 w->setCloseable(isCloseable());
1811 w->setMaximizeable(isMaximizable());
1812 w->setMinimizeable(isMinimizable());
1813 w->setFullscreenable(isFullScreenable());
1814 w->setApplicationMenuPaths(applicationMenuServiceName(), applicationMenuObjectPath());
1815 w->setIcon(icon());
1816 auto updateAppId = [this, w] {
1817 w->setResourceName(resourceName());
1818 w->setAppId(m_desktopFileName.isEmpty() ? resourceClass() : m_desktopFileName);
1819 };
1820 updateAppId();
1821 w->setSkipTaskbar(skipTaskbar());
1822 w->setSkipSwitcher(skipSwitcher());
1823 w->setPid(pid());
1824 w->setShadeable(isShadeable());
1825 w->setShaded(isShade());
1826 w->setResizable(isResizable());
1827 w->setMovable(isMovable());
1828 w->setVirtualDesktopChangeable(true); // FIXME Matches X11Window::actionSupported(), but both should be implemented.
1829 w->setParentWindow(transientFor() ? transientFor()->windowManagementInterface() : nullptr);
1830 w->setGeometry(frameGeometry().toRect());
1831 connect(this, &Window::skipTaskbarChanged, w, [w, this]() {
1832 w->setSkipTaskbar(skipTaskbar());
1833 });
1834 connect(this, &Window::skipSwitcherChanged, w, [w, this]() {
1835 w->setSkipSwitcher(skipSwitcher());
1836 });
1837 connect(this, &Window::captionChanged, w, [w, this] {
1838 w->setTitle(caption());
1839 });
1840
1841 connect(this, &Window::activeChanged, w, [w, this] {
1842 w->setActive(isActive());
1843 });
1844 connect(this, &Window::fullScreenChanged, w, [w, this] {
1845 w->setFullscreen(isFullScreen());
1846 });
1847 connect(this, &Window::keepAboveChanged, w, &PlasmaWindowInterface::setKeepAbove);
1848 connect(this, &Window::keepBelowChanged, w, &PlasmaWindowInterface::setKeepBelow);
1849 connect(this, &Window::minimizedChanged, w, [w, this] {
1850 w->setMinimized(isMinimized());
1851 });
1852 connect(this, &Window::maximizedChanged, w, [w, this]() {
1853 w->setMaximized(maximizeMode() == MaximizeFull);
1854 });
1855 connect(this, &Window::demandsAttentionChanged, w, [w, this] {
1856 w->setDemandsAttention(isDemandingAttention());
1857 });
1858 connect(this, &Window::iconChanged, w, [w, this]() {
1859 w->setIcon(icon());
1860 });
1861 connect(this, &Window::windowClassChanged, w, updateAppId);
1862 connect(this, &Window::desktopFileNameChanged, w, updateAppId);
1863 connect(this, &Window::shadeChanged, w, [w, this] {
1864 w->setShaded(isShade());
1865 });
1866 connect(this, &Window::transientChanged, w, [w, this]() {
1867 w->setParentWindow(transientFor() ? transientFor()->windowManagementInterface() : nullptr);
1868 });
1869 connect(this, &Window::frameGeometryChanged, w, [w, this]() {
1870 w->setGeometry(frameGeometry().toRect());
1871 });
1872 connect(this, &Window::applicationMenuChanged, w, [w, this]() {
1873 w->setApplicationMenuPaths(applicationMenuServiceName(), applicationMenuObjectPath());
1874 });
1875 connect(w, &PlasmaWindowInterface::closeRequested, this, [this] {
1876 closeWindow();
1877 });
1878 connect(w, &PlasmaWindowInterface::moveRequested, this, [this]() {
1879 Cursors::self()->mouse()->setPos(frameGeometry().center());
1880 performMouseCommand(Options::MouseMove, Cursors::self()->mouse()->pos());
1881 });
1882 connect(w, &PlasmaWindowInterface::resizeRequested, this, [this]() {
1883 Cursors::self()->mouse()->setPos(frameGeometry().bottomRight());
1884 performMouseCommand(Options::MouseResize, Cursors::self()->mouse()->pos());
1885 });
1886 connect(w, &PlasmaWindowInterface::fullscreenRequested, this, [this](bool set) {
1887 setFullScreen(set);
1888 });
1889 connect(w, &PlasmaWindowInterface::minimizedRequested, this, [this](bool set) {
1890 setMinimized(set);
1891 });
1892 connect(w, &PlasmaWindowInterface::maximizedRequested, this, [this](bool set) {
1893 maximize(set ? MaximizeFull : MaximizeRestore);
1894 });
1895 connect(w, &PlasmaWindowInterface::keepAboveRequested, this, [this](bool set) {
1896 setKeepAbove(set);
1897 });
1898 connect(w, &PlasmaWindowInterface::keepBelowRequested, this, [this](bool set) {
1899 setKeepBelow(set);
1900 });
1901 connect(w, &PlasmaWindowInterface::demandsAttentionRequested, this, [this](bool set) {
1902 demandAttention(set);
1903 });
1904 connect(w, &PlasmaWindowInterface::activeRequested, this, [this](bool set) {
1905 if (set) {
1906 workspace()->activateWindow(this, true);
1907 }
1908 });
1909 connect(w, &PlasmaWindowInterface::shadedRequested, this, [this](bool set) {
1910 setShade(set);
1911 });
1912
1913 for (const auto vd : std::as_const(m_desktops)) {
1914 w->addPlasmaVirtualDesktop(vd->id());
1915 }
1916 // We need to set `OnAllDesktops` after the actual VD list has been added.
1917 // Otherwise it will unconditionally add the current desktop to the interface
1918 // which may not be the case, for example, when using rules
1919 w->setOnAllDesktops(isOnAllDesktops());
1920
1921 // Plasma Virtual desktop management
1922 // show/hide when the window enters/exits from desktop
1923 connect(w, &PlasmaWindowInterface::enterPlasmaVirtualDesktopRequested, this, [this](const QString &desktopId) {
1924 VirtualDesktop *vd = VirtualDesktopManager::self()->desktopForId(desktopId);
1925 if (vd) {
1926 enterDesktop(vd);
1927 }
1928 });
1929 connect(w, &PlasmaWindowInterface::enterNewPlasmaVirtualDesktopRequested, this, [this]() {
1930 VirtualDesktopManager::self()->setCount(VirtualDesktopManager::self()->count() + 1);
1931 enterDesktop(VirtualDesktopManager::self()->desktops().last());
1932 });
1933 connect(w, &PlasmaWindowInterface::leavePlasmaVirtualDesktopRequested, this, [this](const QString &desktopId) {
1934 VirtualDesktop *vd = VirtualDesktopManager::self()->desktopForId(desktopId);
1935 if (vd) {
1936 leaveDesktop(vd);
1937 }
1938 });
1939
1940 for (const auto &activity : std::as_const(m_activityList)) {
1941 w->addPlasmaActivity(activity);
1942 }
1943
1944 connect(this, &Window::activitiesChanged, w, [w, this] {
1945 const auto newActivities = QSet<QString>(m_activityList.begin(), m_activityList.end());
1946 const auto oldActivitiesList = w->plasmaActivities();
1947 const auto oldActivities = QSet<QString>(oldActivitiesList.begin(), oldActivitiesList.end());
1948
1949 const auto activitiesToAdd = newActivities - oldActivities;
1950 for (const auto &activity : activitiesToAdd) {
1951 w->addPlasmaActivity(activity);
1952 }
1953
1954 const auto activitiesToRemove = oldActivities - newActivities;
1955 for (const auto &activity : activitiesToRemove) {
1956 w->removePlasmaActivity(activity);
1957 }
1958 });
1959
1960 // Plasma Activities management
1961 // show/hide when the window enters/exits activity
1962 connect(w, &PlasmaWindowInterface::enterPlasmaActivityRequested, this, [this](const QString &activityId) {
1963 setOnActivity(activityId, true);
1964 });
1965 connect(w, &PlasmaWindowInterface::leavePlasmaActivityRequested, this, [this](const QString &activityId) {
1966 setOnActivity(activityId, false);
1967 });
1968 connect(w, &PlasmaWindowInterface::sendToOutput, this, [this](OutputInterface *output) {
1969 sendToOutput(output->handle());
1970 });
1971
1972 m_windowManagementInterface = w;
1973}
1974
1975void Window::destroyWindowManagementInterface()
1976{
1977 delete m_windowManagementInterface;
1978 m_windowManagementInterface = nullptr;
1979}
1980
1981Options::MouseCommand Window::getMouseCommand(Qt::MouseButton button, bool *handled) const
1982{
1983 *handled = false;
1984 if (button == Qt::NoButton) {
1985 return Options::MouseNothing;
1986 }
1987 if (isActive()) {
1988 if (options->isClickRaise() && !isMostRecentlyRaised()) {
1989 *handled = true;
1990 return Options::MouseActivateRaiseAndPassClick;
1991 }
1992 } else {
1993 *handled = true;
1994 switch (button) {
1995 case Qt::LeftButton:
1996 return options->commandWindow1();
1997 case Qt::MiddleButton:
1998 return options->commandWindow2();
1999 case Qt::RightButton:
2000 return options->commandWindow3();
2001 default:
2002 // all other buttons pass Activate & Pass Client
2003 return Options::MouseActivateAndPassClick;
2004 }
2005 }
2006 return Options::MouseNothing;
2007}
2008
2009Options::MouseCommand Window::getWheelCommand(Qt::Orientation orientation, bool *handled) const
2010{
2011 *handled = false;
2012 if (orientation != Qt::Vertical) {
2013 return Options::MouseNothing;
2014 }
2015 if (!isActive()) {
2016 *handled = true;
2017 return options->commandWindowWheel();
2018 }
2019 return Options::MouseNothing;
2020}
2021
2022bool Window::performMouseCommand(Options::MouseCommand cmd, const QPointF &globalPos)
2023{
2024 bool replay = false;
2025 switch (cmd) {
2026 case Options::MouseRaise:
2027 workspace()->raiseWindow(this);
2028 break;
2029 case Options::MouseLower: {
2030 workspace()->lowerWindow(this);
2031 // used to be activateNextWindow(this), then topWindowOnDesktop
2032 // since this is a mouseOp it's however safe to use the window under the mouse instead
2033 if (isActive() && options->focusPolicyIsReasonable()) {
2034 Window *next = workspace()->windowUnderMouse(output());
2035 if (next && next != this) {
2036 workspace()->requestFocus(next, false);
2037 }
2038 }
2039 break;
2040 }
2041 case Options::MouseOperationsMenu:
2042 if (isActive() && options->isClickRaise()) {
2043 autoRaise();
2044 }
2045 workspace()->showWindowMenu(QRect(globalPos.toPoint(), globalPos.toPoint()), this);
2046 break;
2047 case Options::MouseToggleRaiseAndLower:
2049 break;
2050 case Options::MouseActivateAndRaise: {
2051 replay = isActive(); // for clickraise mode
2052 bool mustReplay = !rules()->checkAcceptFocus(acceptsFocus());
2053 if (mustReplay) {
2054 auto it = workspace()->stackingOrder().constEnd(),
2055 begin = workspace()->stackingOrder().constBegin();
2056 while (mustReplay && --it != begin && *it != this) {
2057 auto c = *it;
2058 if (!c->isClient() || (c->keepAbove() && !keepAbove()) || (keepBelow() && !c->keepBelow())) {
2059 continue; // can never raise above "it"
2060 }
2061 mustReplay = !(c->isOnCurrentDesktop() && c->isOnCurrentActivity() && c->frameGeometry().intersects(frameGeometry()));
2062 }
2063 }
2064 workspace()->takeActivity(this, Workspace::ActivityFocus | Workspace::ActivityRaise);
2065 workspace()->setActiveOutput(globalPos);
2066 replay = replay || mustReplay;
2067 break;
2068 }
2069 case Options::MouseActivateAndLower:
2070 workspace()->requestFocus(this);
2071 workspace()->lowerWindow(this);
2072 workspace()->setActiveOutput(globalPos);
2073 replay = replay || !rules()->checkAcceptFocus(acceptsFocus());
2074 break;
2075 case Options::MouseActivate:
2076 replay = isActive(); // for clickraise mode
2077 workspace()->takeActivity(this, Workspace::ActivityFocus);
2078 workspace()->setActiveOutput(globalPos);
2079 replay = replay || !rules()->checkAcceptFocus(acceptsFocus());
2080 break;
2081 case Options::MouseActivateRaiseAndPassClick:
2082 workspace()->takeActivity(this, Workspace::ActivityFocus | Workspace::ActivityRaise);
2083 workspace()->setActiveOutput(globalPos);
2084 replay = true;
2085 break;
2086 case Options::MouseActivateAndPassClick:
2087 workspace()->takeActivity(this, Workspace::ActivityFocus);
2088 workspace()->setActiveOutput(globalPos);
2089 replay = true;
2090 break;
2091 case Options::MouseMaximize:
2092 maximize(MaximizeFull);
2093 break;
2094 case Options::MouseRestore:
2095 maximize(MaximizeRestore);
2096 break;
2097 case Options::MouseMinimize:
2098 setMinimized(true);
2099 break;
2100 case Options::MouseAbove: {
2102 if (keepBelow()) {
2103 setKeepBelow(false);
2104 } else {
2105 setKeepAbove(true);
2106 }
2107 break;
2108 }
2109 case Options::MouseBelow: {
2111 if (keepAbove()) {
2112 setKeepAbove(false);
2113 } else {
2114 setKeepBelow(true);
2115 }
2116 break;
2117 }
2118 case Options::MousePreviousDesktop:
2120 break;
2121 case Options::MouseNextDesktop:
2123 break;
2124 case Options::MouseOpacityMore:
2125 if (!isDesktop()) { // No point in changing the opacity of the desktop
2126 setOpacity(std::min(opacity() + 0.1, 1.0));
2127 }
2128 break;
2129 case Options::MouseOpacityLess:
2130 if (!isDesktop()) { // No point in changing the opacity of the desktop
2131 setOpacity(std::max(opacity() - 0.1, 0.1));
2132 }
2133 break;
2134 case Options::MouseClose:
2135 closeWindow();
2136 break;
2137 case Options::MouseActivateRaiseAndMove:
2138 case Options::MouseActivateRaiseAndUnrestrictedMove:
2139 workspace()->raiseWindow(this);
2140 workspace()->requestFocus(this);
2141 workspace()->setActiveOutput(globalPos);
2142 // fallthrough
2143 case Options::MouseMove:
2144 case Options::MouseUnrestrictedMove: {
2145 if (!isMovableAcrossScreens()) {
2146 replay = true;
2147 break;
2148 }
2149 if (isInteractiveMoveResize()) {
2150 finishInteractiveMoveResize(false);
2151 }
2152 setInteractiveMoveResizeGravity(Gravity::None);
2153 setInteractiveMoveResizePointerButtonDown(true);
2154 setInteractiveMoveOffset(QPointF(globalPos.x() - x(), globalPos.y() - y())); // map from global
2155 setInvertedInteractiveMoveOffset(rect().bottomRight() - interactiveMoveOffset());
2156 setUnrestrictedInteractiveMoveResize((cmd == Options::MouseActivateRaiseAndUnrestrictedMove
2157 || cmd == Options::MouseUnrestrictedMove));
2158 if (!startInteractiveMoveResize()) {
2159 setInteractiveMoveResizePointerButtonDown(false);
2160 }
2161 updateCursor();
2162 break;
2163 }
2164 case Options::MouseResize:
2165 case Options::MouseUnrestrictedResize: {
2166 if (!isResizable() || isShade()) {
2167 break;
2168 }
2169 if (isInteractiveMoveResize()) {
2170 finishInteractiveMoveResize(false);
2171 }
2172 setInteractiveMoveResizePointerButtonDown(true);
2173 const QPointF moveOffset = QPointF(globalPos.x() - x(), globalPos.y() - y()); // map from global
2174 setInteractiveMoveOffset(moveOffset);
2175 int x = moveOffset.x(), y = moveOffset.y();
2176 bool left = x < width() / 3;
2177 bool right = x >= 2 * width() / 3;
2178 bool top = y < height() / 3;
2179 bool bot = y >= 2 * height() / 3;
2180 Gravity gravity;
2181 if (top) {
2182 gravity = left ? Gravity::TopLeft : (right ? Gravity::TopRight : Gravity::Top);
2183 } else if (bot) {
2184 gravity = left ? Gravity::BottomLeft : (right ? Gravity::BottomRight : Gravity::Bottom);
2185 } else {
2186 gravity = (x < width() / 2) ? Gravity::Left : Gravity::Right;
2187 }
2188 setInteractiveMoveResizeGravity(gravity);
2189 setInvertedInteractiveMoveOffset(rect().bottomRight() - moveOffset);
2190 setUnrestrictedInteractiveMoveResize((cmd == Options::MouseUnrestrictedResize));
2191 if (!startInteractiveMoveResize()) {
2192 setInteractiveMoveResizePointerButtonDown(false);
2193 }
2194 updateCursor();
2195 break;
2196 }
2197 case Options::MouseShade:
2198 toggleShade();
2199 cancelShadeHoverTimer();
2200 break;
2201 case Options::MouseSetShade:
2202 setShade(ShadeNormal);
2203 cancelShadeHoverTimer();
2204 break;
2205 case Options::MouseUnsetShade:
2206 setShade(ShadeNone);
2207 cancelShadeHoverTimer();
2208 break;
2209 case Options::MouseNothing:
2210 default:
2211 replay = true;
2212 break;
2213 }
2214 return replay;
2215}
2216
2217void Window::setTransientFor(Window *transientFor)
2218{
2219 if (transientFor == this) {
2220 // cannot be transient for one self
2221 return;
2222 }
2223 if (m_transientFor == transientFor) {
2224 return;
2225 }
2226 m_transientFor = transientFor;
2227 Q_EMIT transientChanged();
2228}
2229
2230const Window *Window::transientFor() const
2231{
2232 return m_transientFor;
2233}
2234
2235Window *Window::transientFor()
2236{
2237 return m_transientFor;
2238}
2239
2240bool Window::hasTransientPlacementHint() const
2241{
2242 return false;
2243}
2244
2245QRectF Window::transientPlacement() const
2246{
2247 Q_UNREACHABLE();
2248 return QRectF();
2249}
2250
2251bool Window::hasTransient(const Window *c, bool indirect) const
2252{
2253 return c->transientFor() == this;
2254}
2255
2256QList<Window *> Window::mainWindows() const
2257{
2258 if (const Window *t = transientFor()) {
2259 return QList<Window *>{const_cast<Window *>(t)};
2260 }
2261 return QList<Window *>();
2262}
2263
2264QList<Window *> Window::allMainWindows() const
2265{
2266 auto result = mainWindows();
2267 for (const auto *window : result) {
2268 result += window->allMainWindows();
2269 }
2270 return result;
2271}
2272
2273void Window::setModal(bool m)
2274{
2275 // Qt-3.2 can have even modal normal windows :(
2276 if (m_modal == m) {
2277 return;
2278 }
2279 m_modal = m;
2280 Q_EMIT modalChanged();
2281 // Changing modality for a mapped window is weird (?)
2282 // _NET_WM_STATE_MODAL should possibly rather be _NET_WM_WINDOW_TYPE_MODAL_DIALOG
2283}
2284
2285bool Window::isModal() const
2286{
2287 return m_modal;
2288}
2289
2290bool Window::isTransient() const
2291{
2292 return false;
2293}
2294
2295// check whether a transient should be actually kept above its mainwindow
2296// there may be some special cases where this rule shouldn't be enfored
2297static bool shouldKeepTransientAbove(const Window *parent, const Window *transient)
2298{
2299 // #93832 - don't keep splashscreens above dialogs
2300 if (transient->isSplash() && parent->isDialog()) {
2301 return false;
2302 }
2303 // This is rather a hack for #76026. Don't keep non-modal dialogs above
2304 // the mainwindow, but only if they're group transient (since only such dialogs
2305 // have taskbar entry in Kicker). A proper way of doing this (both kwin and kicker)
2306 // needs to be found.
2307 if (transient->isDialog() && !transient->isModal() && transient->groupTransient()) {
2308 return false;
2309 }
2310 // #63223 - don't keep transients above docks, because the dock is kept high,
2311 // and e.g. dialogs for them would be too high too
2312 // ignore this if the transient has a placement hint which indicates it should go above it's parent
2313 if (parent->isDock() && !transient->hasTransientPlacementHint()) {
2314 return false;
2315 }
2316 return true;
2317}
2318
2319void Window::addTransient(Window *cl)
2320{
2321 Q_ASSERT(!m_transients.contains(cl));
2322 Q_ASSERT(cl != this);
2323 m_transients.append(cl);
2324 if (shouldKeepTransientAbove(this, cl)) {
2325 workspace()->constrain(this, cl);
2326 }
2327}
2328
2329void Window::removeTransient(Window *cl)
2330{
2331 m_transients.removeAll(cl);
2332 if (cl->transientFor() == this) {
2333 cl->setTransientFor(nullptr);
2334 }
2335 workspace()->unconstrain(this, cl);
2336}
2337
2338void Window::removeTransientFromList(Window *cl)
2339{
2340 m_transients.removeAll(cl);
2341}
2342
2343bool Window::isActiveFullScreen() const
2344{
2345 if (!isFullScreen()) {
2346 return false;
2347 }
2348
2349 const auto ac = workspace()->mostRecentlyActivatedWindow(); // instead of activeWindow() - avoids flicker
2350 // according to NETWM spec implementation notes suggests
2351 // "focused windows having state _NET_WM_STATE_FULLSCREEN" to be on the highest layer.
2352 // we'll also take the screen into account
2353 return ac && (ac == this || !ac->isOnOutput(output()) || ac->allMainWindows().contains(const_cast<Window *>(this)));
2354}
2355
2356int Window::borderBottom() const
2357{
2358 return isDecorated() ? decoration()->borderBottom() : 0;
2359}
2360
2361int Window::borderLeft() const
2362{
2363 return isDecorated() ? decoration()->borderLeft() : 0;
2364}
2365
2366int Window::borderRight() const
2367{
2368 return isDecorated() ? decoration()->borderRight() : 0;
2369}
2370
2371int Window::borderTop() const
2372{
2373 return isDecorated() ? decoration()->borderTop() : 0;
2374}
2375
2376void Window::updateCursor()
2377{
2378 Gravity gravity = interactiveMoveResizeGravity();
2379 if (!isResizable() || isShade()) {
2380 gravity = Gravity::None;
2381 }
2382 CursorShape c = Qt::ArrowCursor;
2383 switch (gravity) {
2384 case Gravity::TopLeft:
2386 break;
2387 case Gravity::BottomRight:
2389 break;
2390 case Gravity::BottomLeft:
2392 break;
2393 case Gravity::TopRight:
2395 break;
2396 case Gravity::Top:
2398 break;
2399 case Gravity::Bottom:
2401 break;
2402 case Gravity::Left:
2404 break;
2405 case Gravity::Right:
2407 break;
2408 default:
2409 if (isInteractiveMoveResize()) {
2410 c = Qt::SizeAllCursor;
2411 } else {
2412 c = Qt::ArrowCursor;
2413 }
2414 break;
2415 }
2416 if (c == m_interactiveMoveResize.cursor) {
2417 return;
2418 }
2419 m_interactiveMoveResize.cursor = c;
2420 Q_EMIT moveResizeCursorChanged(c);
2421}
2422
2423void Window::leaveInteractiveMoveResize()
2424{
2425 workspace()->setMoveResizeWindow(nullptr);
2426 setInteractiveMoveResize(false);
2427 if (workspace()->screenEdges()->isDesktopSwitchingMovingClients()) {
2428 workspace()->screenEdges()->reserveDesktopSwitching(false, Qt::Vertical | Qt::Horizontal);
2429 }
2430 if (isElectricBorderMaximizing()) {
2431 workspace()->outline()->hide();
2432 elevate(false);
2433 }
2434}
2435
2436bool Window::doStartInteractiveMoveResize()
2437{
2438 return true;
2439}
2440
2441void Window::doFinishInteractiveMoveResize()
2442{
2443}
2444
2445bool Window::isWaitingForInteractiveMoveResizeSync() const
2446{
2447 return false;
2448}
2449
2450void Window::doInteractiveResizeSync(const QRectF &)
2451{
2452}
2453
2454void Window::checkQuickTilingMaximizationZones(int xroot, int yroot)
2455{
2456 QuickTileMode mode = QuickTileFlag::None;
2457 bool innerBorder = false;
2458
2459 const auto outputs = workspace()->outputs();
2460 for (const Output *output : outputs) {
2461 if (!output->geometry().contains(QPoint(xroot, yroot))) {
2462 continue;
2463 }
2464
2465 auto isInScreen = [&output, &outputs](const QPoint &pt) {
2466 for (const Output *other : outputs) {
2467 if (other == output) {
2468 continue;
2469 }
2470 if (other->geometry().contains(pt)) {
2471 return true;
2472 }
2473 }
2474 return false;
2475 };
2476
2477 QRectF area = workspace()->clientArea(MaximizeArea, this, QPointF(xroot, yroot));
2479 if (xroot <= area.x() + 20) {
2480 mode |= QuickTileFlag::Left;
2481 innerBorder = isInScreen(QPoint(area.x() - 1, yroot));
2482 } else if (xroot >= area.x() + area.width() - 20) {
2483 mode |= QuickTileFlag::Right;
2484 innerBorder = isInScreen(QPoint(area.right() + 1, yroot));
2485 }
2486 }
2487
2488 if (mode != QuickTileMode(QuickTileFlag::None)) {
2489 if (yroot <= area.y() + area.height() * options->electricBorderCornerRatio()) {
2490 mode |= QuickTileFlag::Top;
2491 } else if (yroot >= area.y() + area.height() - area.height() * options->electricBorderCornerRatio()) {
2492 mode |= QuickTileFlag::Bottom;
2493 }
2494 } else if (options->electricBorderMaximize() && yroot <= area.y() + 5 && isMaximizable()) {
2495 mode = QuickTileFlag::Maximize;
2496 innerBorder = isInScreen(QPoint(xroot, area.y() - 1));
2497 }
2498 break; // no point in checking other screens to contain this... "point"...
2499 }
2500 if (mode != electricBorderMode()) {
2501 setElectricBorderMode(mode);
2502 if (innerBorder) {
2503 if (!m_electricMaximizingDelay) {
2504 m_electricMaximizingDelay = new QTimer(this);
2505 m_electricMaximizingDelay->setInterval(250);
2506 m_electricMaximizingDelay->setSingleShot(true);
2507 connect(m_electricMaximizingDelay, &QTimer::timeout, this, [this]() {
2508 if (isInteractiveMove()) {
2509 setElectricBorderMaximizing(electricBorderMode() != QuickTileMode(QuickTileFlag::None));
2510 }
2511 });
2512 }
2513 m_electricMaximizingDelay->start();
2514 } else {
2515 setElectricBorderMaximizing(mode != QuickTileMode(QuickTileFlag::None));
2516 }
2517 }
2518}
2519
2520void Window::resetQuickTilingMaximizationZones()
2521{
2522 if (electricBorderMode() != QuickTileMode(QuickTileFlag::None)) {
2523 if (m_electricMaximizingDelay) {
2524 m_electricMaximizingDelay->stop();
2525 }
2526 setElectricBorderMaximizing(false);
2527 setElectricBorderMode(QuickTileFlag::None);
2528 }
2529}
2530
2531void Window::keyPressEvent(uint key_code)
2532{
2533 if (!isInteractiveMove() && !isInteractiveResize()) {
2534 return;
2535 }
2536 bool is_control = key_code & Qt::CTRL;
2537 bool is_alt = key_code & Qt::ALT;
2538 key_code = key_code & ~Qt::KeyboardModifierMask;
2539 int delta = is_control ? 1 : is_alt ? 32
2540 : 8;
2541 QPointF pos = Cursors::self()->mouse()->pos();
2542 switch (key_code) {
2543 case Qt::Key_Left:
2544 pos.rx() -= delta;
2545 break;
2546 case Qt::Key_Right:
2547 pos.rx() += delta;
2548 break;
2549 case Qt::Key_Up:
2550 pos.ry() -= delta;
2551 break;
2552 case Qt::Key_Down:
2553 pos.ry() += delta;
2554 break;
2555 case Qt::Key_Space:
2556 case Qt::Key_Return:
2557 case Qt::Key_Enter:
2558 setInteractiveMoveResizePointerButtonDown(false);
2559 finishInteractiveMoveResize(false);
2560 updateCursor();
2561 break;
2562 case Qt::Key_Escape:
2563 setInteractiveMoveResizePointerButtonDown(false);
2564 finishInteractiveMoveResize(true);
2565 updateCursor();
2566 break;
2567 default:
2568 return;
2569 }
2570 Cursors::self()->mouse()->setPos(pos);
2571}
2572
2573QSizeF Window::resizeIncrements() const
2574{
2575 return QSizeF(1, 1);
2576}
2577
2578void Window::dontInteractiveMoveResize()
2579{
2580 setInteractiveMoveResizePointerButtonDown(false);
2581 stopDelayedInteractiveMoveResize();
2582 if (isInteractiveMoveResize()) {
2583 finishInteractiveMoveResize(false);
2584 }
2585}
2586
2587Gravity Window::mouseGravity() const
2588{
2589 if (isDecorated()) {
2590 switch (decoration()->sectionUnderMouse()) {
2591 case Qt::BottomLeftSection:
2592 return Gravity::BottomLeft;
2593 case Qt::BottomRightSection:
2594 return Gravity::BottomRight;
2595 case Qt::BottomSection:
2596 return Gravity::Bottom;
2597 case Qt::LeftSection:
2598 return Gravity::Left;
2599 case Qt::RightSection:
2600 return Gravity::Right;
2601 case Qt::TopSection:
2602 return Gravity::Top;
2603 case Qt::TopLeftSection:
2604 return Gravity::TopLeft;
2605 case Qt::TopRightSection:
2606 return Gravity::TopRight;
2607 default:
2608 return Gravity::None;
2609 }
2610 }
2611 return Gravity::None;
2612}
2613
2614void Window::endInteractiveMoveResize()
2615{
2616 setInteractiveMoveResizePointerButtonDown(false);
2617 stopDelayedInteractiveMoveResize();
2618 if (isInteractiveMoveResize()) {
2619 finishInteractiveMoveResize(false);
2620 setInteractiveMoveResizeGravity(mouseGravity());
2621 }
2622 updateCursor();
2623}
2624
2625void Window::setDecoration(std::shared_ptr<KDecoration2::Decoration> decoration)
2626{
2627 if (m_decoration.decoration == decoration) {
2628 return;
2629 }
2630 if (decoration) {
2631 QMetaObject::invokeMethod(decoration.get(), QOverload<>::of(&KDecoration2::Decoration::update), Qt::QueuedConnection);
2632 connect(decoration.get(), &KDecoration2::Decoration::shadowChanged, this, [this]() {
2633 if (!isDeleted()) {
2634 updateShadow();
2635 }
2636 });
2637 connect(decoration.get(), &KDecoration2::Decoration::bordersChanged, this, [this]() {
2638 if (!isDeleted()) {
2639 updateDecorationInputShape();
2640 }
2641 });
2642 connect(decoration.get(), &KDecoration2::Decoration::resizeOnlyBordersChanged, this, [this]() {
2643 if (!isDeleted()) {
2644 updateDecorationInputShape();
2645 }
2646 });
2647 connect(decoratedClient()->decoratedClient(), &KDecoration2::DecoratedClient::sizeChanged, this, [this]() {
2648 if (!isDeleted()) {
2649 updateDecorationInputShape();
2650 }
2651 });
2652 }
2653 m_decoration.decoration = decoration;
2654 updateDecorationInputShape();
2655 Q_EMIT decorationChanged();
2656}
2657
2658void Window::updateDecorationInputShape()
2659{
2660 if (!isDecorated()) {
2661 m_decoration.inputRegion = QRegion();
2662 return;
2663 }
2664
2665 const QMargins borders = decoration()->borders();
2666 const QMargins resizeBorders = decoration()->resizeOnlyBorders();
2667
2668 const QRectF innerRect = QRectF(QPointF(borderLeft(), borderTop()), decoratedClient()->size());
2669 const QRectF outerRect = innerRect + borders + resizeBorders;
2670
2671 m_decoration.inputRegion = QRegion(outerRect.toAlignedRect()) - innerRect.toAlignedRect();
2672}
2673
2674bool Window::decorationHasAlpha() const
2675{
2676 if (!isDecorated() || decoration()->isOpaque()) {
2677 // either no decoration or decoration has alpha disabled
2678 return false;
2679 }
2680 return true;
2681}
2682
2683void Window::triggerDecorationRepaint()
2684{
2685 if (isDecorated()) {
2686 decoration()->update();
2687 }
2688}
2689
2690void Window::layoutDecorationRects(QRectF &left, QRectF &top, QRectF &right, QRectF &bottom) const
2691{
2692 if (!isDecorated()) {
2693 return;
2694 }
2695 QRectF r = decoration()->rect();
2696
2697 top = QRectF(r.x(), r.y(), r.width(), borderTop());
2698 bottom = QRectF(r.x(), r.y() + r.height() - borderBottom(),
2699 r.width(), borderBottom());
2700 left = QRectF(r.x(), r.y() + top.height(),
2701 borderLeft(), r.height() - top.height() - bottom.height());
2702 right = QRectF(r.x() + r.width() - borderRight(), r.y() + top.height(),
2703 borderRight(), r.height() - top.height() - bottom.height());
2704}
2705
2706void Window::processDecorationMove(const QPointF &localPos, const QPointF &globalPos)
2707{
2708 if (isInteractiveMoveResizePointerButtonDown()) {
2709 handleInteractiveMoveResize(localPos.x(), localPos.y(), globalPos.x(), globalPos.y());
2710 return;
2711 }
2712 // TODO: handle modifiers
2713 Gravity newGravity = mouseGravity();
2714 if (newGravity != interactiveMoveResizeGravity()) {
2715 setInteractiveMoveResizeGravity(newGravity);
2716 updateCursor();
2717 }
2718}
2719
2720bool Window::processDecorationButtonPress(QMouseEvent *event, bool ignoreMenu)
2721{
2722 Options::MouseCommand com = Options::MouseNothing;
2723 bool active = isActive();
2724 if (!wantsInput()) { // we cannot be active, use it anyway
2725 active = true;
2726 }
2727
2728 // check whether it is a double click
2729 if (event->button() == Qt::LeftButton && titlebarPositionUnderMouse()) {
2730 if (m_decoration.doubleClickTimer.isValid()) {
2731 const qint64 interval = m_decoration.doubleClickTimer.elapsed();
2732 m_decoration.doubleClickTimer.invalidate();
2733 if (interval > QGuiApplication::styleHints()->mouseDoubleClickInterval()) {
2734 m_decoration.doubleClickTimer.start(); // expired -> new first click and pot. init
2735 } else {
2736 Workspace::self()->performWindowOperation(this, options->operationTitlebarDblClick());
2737 dontInteractiveMoveResize();
2738 return false;
2739 }
2740 } else {
2741 m_decoration.doubleClickTimer.start(); // new first click and pot. init, could be invalidated by release - see below
2742 }
2743 }
2744
2745 if (event->button() == Qt::LeftButton) {
2747 } else if (event->button() == Qt::MiddleButton) {
2749 } else if (event->button() == Qt::RightButton) {
2751 }
2752 if (event->button() == Qt::LeftButton
2753 && com != Options::MouseOperationsMenu // actions where it's not possible to get the matching
2754 && com != Options::MouseMinimize) // mouse release event
2755 {
2756 setInteractiveMoveResizeGravity(mouseGravity());
2757 setInteractiveMoveResizePointerButtonDown(true);
2758 setInteractiveMoveOffset(event->pos());
2759 setInvertedInteractiveMoveOffset(rect().bottomRight() - interactiveMoveOffset());
2760 setUnrestrictedInteractiveMoveResize(false);
2761 startDelayedInteractiveMoveResize();
2762 updateCursor();
2763 }
2764 // In the new API the decoration may process the menu action to display an inactive tab's menu.
2765 // If the event is unhandled then the core will create one for the active window in the group.
2766 if (!ignoreMenu || com != Options::MouseOperationsMenu) {
2767 performMouseCommand(com, event->globalPos());
2768 }
2769 return !( // Return events that should be passed to the decoration in the new API
2770 com == Options::MouseRaise || com == Options::MouseOperationsMenu || com == Options::MouseActivateAndRaise || com == Options::MouseActivate || com == Options::MouseActivateRaiseAndPassClick || com == Options::MouseActivateAndPassClick || com == Options::MouseNothing);
2771}
2772
2773void Window::processDecorationButtonRelease(QMouseEvent *event)
2774{
2775 if (isDecorated()) {
2776 if (event->isAccepted() || !titlebarPositionUnderMouse()) {
2777 invalidateDecorationDoubleClickTimer(); // click was for the deco and shall not init a doubleclick
2778 }
2779 }
2780
2781 if (event->buttons() == Qt::NoButton) {
2782 setInteractiveMoveResizePointerButtonDown(false);
2783 stopDelayedInteractiveMoveResize();
2784 if (isInteractiveMoveResize()) {
2785 finishInteractiveMoveResize(false);
2786 setInteractiveMoveResizeGravity(mouseGravity());
2787 }
2788 updateCursor();
2789 }
2790}
2791
2792void Window::startDecorationDoubleClickTimer()
2793{
2794 m_decoration.doubleClickTimer.start();
2795}
2796
2797void Window::invalidateDecorationDoubleClickTimer()
2798{
2799 m_decoration.doubleClickTimer.invalidate();
2800}
2801
2802bool Window::providesContextHelp() const
2803{
2804 return false;
2805}
2806
2807void Window::showContextHelp()
2808{
2809}
2810
2811Decoration::DecoratedClientImpl *Window::decoratedClient() const
2812{
2813 return m_decoration.client;
2814}
2815
2816void Window::setDecoratedClient(Decoration::DecoratedClientImpl *client)
2817{
2818 m_decoration.client = client;
2819}
2820
2821void Window::pointerEnterEvent(const QPointF &globalPos)
2822{
2823 if (options->isShadeHover()) {
2824 cancelShadeHoverTimer();
2825 startShadeHoverTimer();
2826 }
2827
2828 if (options->focusPolicy() == Options::ClickToFocus || workspace()->userActionsMenu()->isShown()) {
2829 return;
2830 }
2831
2832 if (options->isAutoRaise() && !isDesktop() && !isDock() && workspace()->focusChangeEnabled() && globalPos != workspace()->focusMousePosition() && workspace()->topWindowOnDesktop(VirtualDesktopManager::self()->currentDesktop(), options->isSeparateScreenFocus() ? output() : nullptr) != this) {
2833 startAutoRaise();
2834 }
2835
2836 if (isDesktop() || isDock()) {
2837 return;
2838 }
2839 // for FocusFollowsMouse, change focus only if the mouse has actually been moved, not if the focus
2840 // change came because of window changes (e.g. closing a window) - #92290
2841 if (options->focusPolicy() != Options::FocusFollowsMouse
2842 || globalPos != workspace()->focusMousePosition()) {
2844 }
2845}
2846
2847void Window::pointerLeaveEvent()
2848{
2849 cancelAutoRaise();
2851 cancelShadeHoverTimer();
2852 startShadeUnhoverTimer();
2853 // TODO: send hover leave to deco
2854 // TODO: handle Options::FocusStrictlyUnderMouse
2855}
2856
2857QRectF Window::iconGeometry() const
2858{
2859 if (!windowManagementInterface() || !waylandServer()) {
2860 // window management interface is only available if the surface is mapped
2861 return QRectF();
2862 }
2863
2864 int minDistance = INT_MAX;
2865 Window *candidatePanel = nullptr;
2866 QRectF candidateGeom;
2867
2868 const auto minGeometries = windowManagementInterface()->minimizedGeometries();
2869 for (auto i = minGeometries.constBegin(), end = minGeometries.constEnd(); i != end; ++i) {
2870 Window *panel = waylandServer()->findWindow(i.key());
2871 if (!panel) {
2872 continue;
2873 }
2874 const int distance = QPointF(panel->pos() - pos()).manhattanLength();
2875 if (distance < minDistance) {
2876 minDistance = distance;
2877 candidatePanel = panel;
2878 candidateGeom = i.value();
2879 }
2880 }
2881 if (!candidatePanel) {
2882 // Check all mainwindows of this window.
2883 const auto windows = mainWindows();
2884 for (Window *mainWindow : windows) {
2885 const auto geom = mainWindow->iconGeometry();
2886 if (geom.isValid()) {
2887 return geom;
2888 }
2889 }
2890 return QRectF();
2891 }
2892 return candidateGeom.translated(candidatePanel->pos());
2893}
2894
2895QRectF Window::virtualKeyboardGeometry() const
2896{
2897 return m_virtualKeyboardGeometry;
2898}
2899
2900void Window::setVirtualKeyboardGeometry(const QRectF &geo)
2901{
2902 // No keyboard anymore
2903 if (geo.isEmpty() && !m_keyboardGeometryRestore.isEmpty()) {
2904 const QRectF availableArea = workspace()->clientArea(MaximizeArea, this);
2905 QRectF newWindowGeometry = (requestedMaximizeMode() & MaximizeHorizontal) ? availableArea : m_keyboardGeometryRestore;
2906 moveResize(newWindowGeometry);
2907 m_keyboardGeometryRestore = QRectF();
2908 } else if (geo.isEmpty()) {
2909 return;
2910 // The keyboard has just been opened (rather than resized) save window geometry for a restore
2911 } else if (m_keyboardGeometryRestore.isEmpty()) {
2912 m_keyboardGeometryRestore = moveResizeGeometry();
2913 }
2914
2915 m_virtualKeyboardGeometry = geo;
2916
2917 // Don't resize Desktop and fullscreen windows
2918 if (isRequestedFullScreen() || isDesktop()) {
2919 return;
2920 }
2921
2922 if (!geo.intersects(m_keyboardGeometryRestore)) {
2923 return;
2924 }
2925
2926 const QRectF availableArea = workspace()->clientArea(MaximizeArea, this);
2927 QRectF newWindowGeometry = (requestedMaximizeMode() & MaximizeHorizontal) ? availableArea : m_keyboardGeometryRestore;
2928 newWindowGeometry.setHeight(std::min(newWindowGeometry.height(), geo.top() - availableArea.top()));
2929 newWindowGeometry.moveTop(std::max(geo.top() - newWindowGeometry.height(), availableArea.top()));
2930 newWindowGeometry = newWindowGeometry.intersected(availableArea);
2931 moveResize(newWindowGeometry);
2932}
2933
2934QRectF Window::keyboardGeometryRestore() const
2935{
2936 return m_keyboardGeometryRestore;
2937}
2938
2939void Window::setKeyboardGeometryRestore(const QRectF &geom)
2940{
2941 m_keyboardGeometryRestore = geom;
2942}
2943
2944bool Window::dockWantsInput() const
2945{
2946 return false;
2947}
2948
2949void Window::setDesktopFileName(const QString &name)
2950{
2951 const QString effectiveName = rules()->checkDesktopFile(name);
2952 if (effectiveName == m_desktopFileName) {
2953 return;
2954 }
2955 m_desktopFileName = effectiveName;
2956 updateWindowRules(Rules::DesktopFile);
2957 Q_EMIT desktopFileNameChanged();
2958}
2959
2960QString Window::iconFromDesktopFile(const QString &desktopFileName)
2961{
2962 const QString absolutePath = findDesktopFile(desktopFileName);
2963 if (absolutePath.isEmpty()) {
2964 return {};
2965 }
2966
2967 KDesktopFile df(absolutePath);
2968 return df.readIcon();
2969}
2970
2971QString Window::iconFromDesktopFile() const
2972{
2973 return iconFromDesktopFile(m_desktopFileName);
2974}
2975
2976QString Window::findDesktopFile(const QString &desktopFileName)
2977{
2978 if (desktopFileName.isEmpty()) {
2979 return {};
2980 }
2981
2982 const QString desktopFileNameWithPrefix = desktopFileName + QLatin1String(".desktop");
2983 QString desktopFilePath;
2984
2985 if (QDir::isAbsolutePath(desktopFileName)) {
2986 if (QFile::exists(desktopFileNameWithPrefix)) {
2987 desktopFilePath = desktopFileNameWithPrefix;
2988 } else {
2989 desktopFilePath = desktopFileName;
2990 }
2991 }
2992
2993 if (desktopFilePath.isEmpty()) {
2994 desktopFilePath = QStandardPaths::locate(QStandardPaths::ApplicationsLocation,
2995 desktopFileNameWithPrefix);
2996 }
2997 if (desktopFilePath.isEmpty()) {
2998 desktopFilePath = QStandardPaths::locate(QStandardPaths::ApplicationsLocation,
2999 desktopFileName);
3000 }
3001 return desktopFilePath;
3002}
3003
3004bool Window::hasApplicationMenu() const
3005{
3006 return Workspace::self()->applicationMenu()->applicationMenuEnabled() && !m_applicationMenuServiceName.isEmpty() && !m_applicationMenuObjectPath.isEmpty();
3007}
3008
3009void Window::updateApplicationMenuServiceName(const QString &serviceName)
3010{
3011 const bool old_hasApplicationMenu = hasApplicationMenu();
3012
3013 m_applicationMenuServiceName = serviceName;
3014
3015 const bool new_hasApplicationMenu = hasApplicationMenu();
3016
3017 Q_EMIT applicationMenuChanged();
3018 if (old_hasApplicationMenu != new_hasApplicationMenu) {
3019 Q_EMIT hasApplicationMenuChanged(new_hasApplicationMenu);
3020 }
3021}
3022
3023void Window::updateApplicationMenuObjectPath(const QString &objectPath)
3024{
3025 const bool old_hasApplicationMenu = hasApplicationMenu();
3026
3027 m_applicationMenuObjectPath = objectPath;
3028
3029 const bool new_hasApplicationMenu = hasApplicationMenu();
3030
3031 Q_EMIT applicationMenuChanged();
3032 if (old_hasApplicationMenu != new_hasApplicationMenu) {
3033 Q_EMIT hasApplicationMenuChanged(new_hasApplicationMenu);
3034 }
3035}
3036
3037void Window::setApplicationMenuActive(bool applicationMenuActive)
3038{
3039 if (m_applicationMenuActive != applicationMenuActive) {
3040 m_applicationMenuActive = applicationMenuActive;
3041 Q_EMIT applicationMenuActiveChanged(applicationMenuActive);
3042 }
3043}
3044
3045void Window::showApplicationMenu(int actionId)
3046{
3047 if (isDecorated()) {
3048 decoration()->showApplicationMenu(actionId);
3049 } else {
3050 // we don't know where the application menu button will be, show it in the top left corner instead
3051 Workspace::self()->showApplicationMenu(QRect(), this, actionId);
3052 }
3053}
3054
3055bool Window::unresponsive() const
3056{
3057 return m_unresponsive;
3058}
3059
3060void Window::setUnresponsive(bool unresponsive)
3061{
3062 if (m_unresponsive != unresponsive) {
3063 m_unresponsive = unresponsive;
3064 Q_EMIT unresponsiveChanged(m_unresponsive);
3065 Q_EMIT captionChanged();
3066 }
3067}
3068
3069QString Window::shortcutCaptionSuffix() const
3070{
3071 if (shortcut().isEmpty()) {
3072 return QString();
3073 }
3074 return QLatin1String(" {") + shortcut().toString() + QLatin1Char('}');
3075}
3076
3077QString Window::caption() const
3078{
3079 QString cap = captionNormal() + captionSuffix();
3080 if (unresponsive()) {
3081 cap += QLatin1String(" ");
3082 cap += i18nc("Application is not responding, appended to window title", "(Not Responding)");
3083 }
3084 return cap;
3085}
3086
3092QStringList Window::activities() const
3093{
3094 return m_activityList;
3095}
3096
3097bool Window::isOnCurrentActivity() const
3098{
3099#if KWIN_BUILD_ACTIVITIES
3100 if (!Workspace::self()->activities()) {
3101 return true;
3102 }
3103 return isOnActivity(Workspace::self()->activities()->current());
3104#else
3105 return true;
3106#endif
3107}
3108
3116void Window::setOnActivity(const QString &activity, bool enable)
3117{
3118#if KWIN_BUILD_ACTIVITIES
3119 if (!Workspace::self()->activities()) {
3120 return;
3121 }
3122 QStringList newActivitiesList = activities();
3123 if (newActivitiesList.contains(activity) == enable) {
3124 // nothing to do
3125 return;
3126 }
3127 if (enable) {
3128 QStringList allActivities = Workspace::self()->activities()->all();
3129 if (!allActivities.contains(activity)) {
3130 // bogus ID
3131 return;
3132 }
3133 newActivitiesList.append(activity);
3134 } else {
3135 newActivitiesList.removeOne(activity);
3136 }
3137 setOnActivities(newActivitiesList);
3138#endif
3139}
3140
3144void Window::setOnActivities(const QStringList &newActivitiesList)
3145{
3146#if KWIN_BUILD_ACTIVITIES
3147 if (!Workspace::self()->activities()) {
3148 return;
3149 }
3150 if (Workspace::self()->activities()->serviceStatus() != KActivities::Consumer::Running) {
3151 return;
3152 }
3153 const auto allActivities = Workspace::self()->activities()->all();
3154 const auto activityList = [&] {
3155 auto result = rules()->checkActivity(newActivitiesList);
3156
3157 const auto it = std::remove_if(result.begin(), result.end(), [=](const QString &activity) {
3158 return !allActivities.contains(activity);
3159 });
3160 result.erase(it, result.end());
3161 return result;
3162 }();
3163
3164 const auto allActivityExplicitlyRequested = activityList.isEmpty() || activityList.contains(Activities::nullUuid());
3165 const auto allActivitiesCovered = activityList.size() > 1 && activityList.size() == allActivities.size();
3166
3167 if (allActivityExplicitlyRequested || allActivitiesCovered) {
3168 if (!m_activityList.isEmpty()) {
3169 m_activityList.clear();
3170 doSetOnActivities(m_activityList);
3171 }
3172 } else {
3173 if (m_activityList != activityList) {
3174 m_activityList = activityList;
3175 doSetOnActivities(m_activityList);
3176 }
3177 }
3178
3179 updateActivities(false);
3180#endif
3181}
3182
3183void Window::doSetOnActivities(const QStringList &activityList)
3184{
3185}
3186
3191void Window::setOnAllActivities(bool all)
3192{
3193#if KWIN_BUILD_ACTIVITIES
3194 if (all == isOnAllActivities()) {
3195 return;
3196 }
3197 if (all) {
3198 setOnActivities(QStringList());
3199 } else {
3200 setOnActivity(Workspace::self()->activities()->current(), true);
3201 }
3202#endif
3203}
3204
3208void Window::updateActivities(bool includeTransients)
3209{
3210 if (m_activityUpdatesBlocked) {
3211 m_blockedActivityUpdatesRequireTransients |= includeTransients;
3212 return;
3213 }
3214 Q_EMIT activitiesChanged();
3215 m_blockedActivityUpdatesRequireTransients = false; // reset
3216 Workspace::self()->focusChain()->update(this, FocusChain::MakeFirst);
3217 updateWindowRules(Rules::Activity);
3218}
3219
3220void Window::blockActivityUpdates(bool b)
3221{
3222 if (b) {
3223 ++m_activityUpdatesBlocked;
3224 } else {
3225 Q_ASSERT(m_activityUpdatesBlocked);
3226 --m_activityUpdatesBlocked;
3227 if (!m_activityUpdatesBlocked) {
3228 updateActivities(m_blockedActivityUpdatesRequireTransients);
3229 }
3230 }
3231}
3232
3233bool Window::groupTransient() const
3234{
3235 return false;
3236}
3237
3238const Group *Window::group() const
3239{
3240 return nullptr;
3241}
3242
3243Group *Window::group()
3244{
3245 return nullptr;
3246}
3247
3248QPointF Window::framePosToClientPos(const QPointF &point) const
3249{
3250 return point + QPointF(borderLeft(), borderTop());
3251}
3252
3253QPointF Window::clientPosToFramePos(const QPointF &point) const
3254{
3255 return point - QPointF(borderLeft(), borderTop());
3256}
3257
3258QSizeF Window::frameSizeToClientSize(const QSizeF &size) const
3259{
3260 const qreal width = size.width() - borderLeft() - borderRight();
3261 const qreal height = size.height() - borderTop() - borderBottom();
3262 return QSizeF(width, height);
3263}
3264
3265QSizeF Window::clientSizeToFrameSize(const QSizeF &size) const
3266{
3267 const qreal width = size.width() + borderLeft() + borderRight();
3268 const qreal height = size.height() + borderTop() + borderBottom();
3269 return QSizeF(width, height);
3270}
3271
3272QRectF Window::frameRectToClientRect(const QRectF &rect) const
3273{
3274 const QPointF position = framePosToClientPos(rect.topLeft());
3275 const QSizeF size = frameSizeToClientSize(rect.size());
3276 return QRectF(position, size);
3277}
3278
3279QRectF Window::clientRectToFrameRect(const QRectF &rect) const
3280{
3281 const QPointF position = clientPosToFramePos(rect.topLeft());
3282 const QSizeF size = clientSizeToFrameSize(rect.size());
3283 return QRectF(position, size);
3284}
3285
3286QRectF Window::moveResizeGeometry() const
3287{
3288 return m_moveResizeGeometry;
3289}
3290
3291void Window::setMoveResizeGeometry(const QRectF &geo)
3292{
3293 m_moveResizeGeometry = geo;
3294 m_moveResizeOutput = workspace()->outputAt(geo.center());
3295}
3296
3297Output *Window::moveResizeOutput() const
3298{
3299 return m_moveResizeOutput;
3300}
3301
3302void Window::setMoveResizeOutput(Output *output)
3303{
3304 m_moveResizeOutput = output;
3305}
3306
3307void Window::move(const QPointF &point)
3308{
3309 const QRectF rect = QRectF(point, m_moveResizeGeometry.size());
3310
3311 setMoveResizeGeometry(rect);
3312 moveResizeInternal(rect, MoveResizeMode::Move);
3313}
3314
3315void Window::resize(const QSizeF &size)
3316{
3317 const QRectF rect = QRectF(m_moveResizeGeometry.topLeft(), size);
3318
3319 setMoveResizeGeometry(rect);
3320 moveResizeInternal(rect, MoveResizeMode::Resize);
3321}
3322
3323void Window::moveResize(const QRectF &rect)
3324{
3325 setMoveResizeGeometry(rect);
3326 moveResizeInternal(rect, MoveResizeMode::MoveResize);
3327}
3328
3329void Window::setElectricBorderMode(QuickTileMode mode)
3330{
3331 if (mode != QuickTileMode(QuickTileFlag::Maximize)) {
3332 // sanitize the mode, ie. simplify "invalid" combinations
3333 if ((mode & QuickTileFlag::Horizontal) == QuickTileMode(QuickTileFlag::Horizontal)) {
3334 mode &= ~QuickTileMode(QuickTileFlag::Horizontal);
3335 }
3336 if ((mode & QuickTileFlag::Vertical) == QuickTileMode(QuickTileFlag::Vertical)) {
3337 mode &= ~QuickTileMode(QuickTileFlag::Vertical);
3338 }
3339 }
3340 m_electricMode = mode;
3341}
3342
3343void Window::setElectricBorderMaximizing(bool maximizing)
3344{
3345 m_electricMaximizing = maximizing;
3346 if (maximizing) {
3347 workspace()->outline()->show(quickTileGeometry(electricBorderMode(), Cursors::self()->mouse()->pos()).toRect(), moveResizeGeometry().toRect());
3348 } else {
3349 workspace()->outline()->hide();
3350 }
3351 elevate(maximizing);
3352}
3353
3354QRectF Window::quickTileGeometry(QuickTileMode mode, const QPointF &pos) const
3355{
3356 if (mode == QuickTileMode(QuickTileFlag::Maximize)) {
3357 if (requestedMaximizeMode() == MaximizeFull) {
3358 return geometryRestore();
3359 } else {
3360 return workspace()->clientArea(MaximizeArea, this, pos);
3361 }
3362 }
3363
3364 Output *output = workspace()->outputAt(pos);
3365
3366 if (mode & QuickTileFlag::Custom) {
3367 Tile *tile = workspace()->tileManager(output)->bestTileForPosition(pos);
3368 if (tile) {
3369 return tile->windowGeometry();
3370 } else {
3371 return QRectF();
3372 }
3373 }
3374
3375 Tile *tile = workspace()->tileManager(output)->quickTile(mode);
3376 if (tile) {
3377 return tile->windowGeometry();
3378 }
3379 return workspace()->clientArea(MaximizeArea, this, pos);
3380}
3381
3382void Window::updateElectricGeometryRestore()
3383{
3384 m_electricGeometryRestore = geometryRestore();
3385 if (m_interactiveMoveResize.initialQuickTileMode == QuickTileMode(QuickTileFlag::None)) {
3386 if (!(requestedMaximizeMode() & MaximizeHorizontal)) {
3387 m_electricGeometryRestore.setX(x());
3388 m_electricGeometryRestore.setWidth(width());
3389 }
3390 if (!(requestedMaximizeMode() & MaximizeVertical)) {
3391 m_electricGeometryRestore.setY(y());
3392 m_electricGeometryRestore.setHeight(height());
3393 }
3394 }
3395}
3396
3397QRectF Window::quickTileGeometryRestore() const
3398{
3399 if (quickTileMode() != QuickTileMode(QuickTileFlag::None)) {
3400 // If the window is tiled, geometryRestore() already has a good value.
3401 return geometryRestore();
3402 }
3403
3404 if (isElectricBorderMaximizing()) {
3405 return m_electricGeometryRestore;
3406 } else {
3407 return moveResizeGeometry();
3408 }
3409}
3410
3411void Window::setQuickTileMode(QuickTileMode mode, bool keyboard)
3412{
3413 // Only allow quick tile on a regular window.
3414 if (!isResizable()) {
3415 return;
3416 }
3417 if (isAppletPopup()) {
3418 return;
3419 }
3420
3421 workspace()->updateFocusMousePosition(Cursors::self()->mouse()->pos()); // may cause leave event
3422
3423 GeometryUpdatesBlocker blocker(this);
3424
3425 setTile(nullptr);
3426
3427 if (mode == QuickTileMode(QuickTileFlag::Maximize)) {
3428 if (requestedMaximizeMode() == MaximizeFull) {
3429 m_quickTileMode = int(QuickTileFlag::None);
3430 setMaximize(false, false);
3431 } else {
3432 QRectF effectiveGeometryRestore = quickTileGeometryRestore();
3433 m_quickTileMode = int(QuickTileFlag::Maximize);
3434 setMaximize(true, true);
3435 setGeometryRestore(effectiveGeometryRestore);
3436 }
3437 doSetQuickTileMode();
3438 Q_EMIT quickTileModeChanged();
3439 return;
3440 }
3441
3442 // sanitize the mode, ie. simplify "invalid" combinations
3443 if ((mode & QuickTileFlag::Horizontal) == QuickTileMode(QuickTileFlag::Horizontal)) {
3444 mode &= ~QuickTileMode(QuickTileFlag::Horizontal);
3445 }
3446 if ((mode & QuickTileFlag::Vertical) == QuickTileMode(QuickTileFlag::Vertical)) {
3447 mode &= ~QuickTileMode(QuickTileFlag::Vertical);
3448 }
3449
3450 // restore from maximized so that it is possible to tile maximized windows with one hit or by dragging
3451 if (requestedMaximizeMode() != MaximizeRestore) {
3452
3453 if (mode != QuickTileMode(QuickTileFlag::None)) {
3454 m_quickTileMode = int(QuickTileFlag::None); // Temporary, so the maximize code doesn't get all confused
3455
3456 setMaximize(false, false);
3457
3458 moveResize(quickTileGeometry(mode, keyboard ? moveResizeGeometry().center() : Cursors::self()->mouse()->pos()));
3459 // Store the mode change
3460 m_quickTileMode = mode;
3461 } else {
3462 m_quickTileMode = mode;
3463 setMaximize(false, false);
3464 }
3465
3466 doSetQuickTileMode();
3467 Q_EMIT quickTileModeChanged();
3468
3469 return;
3470 }
3471
3472 QPointF whichScreen = keyboard ? moveResizeGeometry().center() : Cursors::self()->mouse()->pos();
3473 if (mode != QuickTileMode(QuickTileFlag::None)) {
3474 // If trying to tile to the side that the window is already tiled to move the window to the next
3475 // screen near the tile if it exists and swap the tile side, otherwise toggle the mode (set QuickTileFlag::None)
3476 if (quickTileMode() == mode) {
3477 Output *currentOutput = moveResizeOutput();
3478 Output *nextOutput = currentOutput;
3479 Output *candidateOutput = currentOutput;
3480 if ((mode & QuickTileFlag::Horizontal) == QuickTileMode(QuickTileFlag::Left)) {
3481 candidateOutput = workspace()->findOutput(nextOutput, Workspace::DirectionWest);
3482 } else if ((mode & QuickTileFlag::Horizontal) == QuickTileMode(QuickTileFlag::Right)) {
3483 candidateOutput = workspace()->findOutput(nextOutput, Workspace::DirectionEast);
3484 }
3485 bool shiftHorizontal = candidateOutput != nextOutput;
3486 nextOutput = candidateOutput;
3487 if ((mode & QuickTileFlag::Vertical) == QuickTileMode(QuickTileFlag::Top)) {
3488 candidateOutput = workspace()->findOutput(nextOutput, Workspace::DirectionNorth);
3489 } else if ((mode & QuickTileFlag::Vertical) == QuickTileMode(QuickTileFlag::Bottom)) {
3490 candidateOutput = workspace()->findOutput(nextOutput, Workspace::DirectionSouth);
3491 }
3492 bool shiftVertical = candidateOutput != nextOutput;
3493 nextOutput = candidateOutput;
3494
3495 if (nextOutput == currentOutput) {
3496 mode = QuickTileFlag::None; // No other screens in the tile direction, toggle tiling
3497 } else {
3498 // Move to other screen
3499 moveResize(geometryRestore().translated(nextOutput->geometry().topLeft() - currentOutput->geometry().topLeft()));
3500 whichScreen = nextOutput->geometry().center();
3501
3502 // Swap sides
3503 if (shiftHorizontal) {
3504 mode = (~mode & QuickTileFlag::Horizontal) | (mode & QuickTileFlag::Vertical);
3505 }
3506 if (shiftVertical) {
3507 mode = (~mode & QuickTileFlag::Vertical) | (mode & QuickTileFlag::Horizontal);
3508 }
3509 }
3510 } else if (quickTileMode() == QuickTileMode(QuickTileFlag::None)) {
3511 // Not coming out of an existing tile, not shifting monitors, we're setting a brand new tile.
3512 // Store geometry first, so we can go out of this tile later.
3513 setGeometryRestore(quickTileGeometryRestore());
3514 }
3515
3516 m_quickTileMode = mode;
3517 }
3518
3519 if (mode == QuickTileMode(QuickTileFlag::None)) {
3520 setTile(nullptr);
3521 m_quickTileMode = int(QuickTileFlag::None);
3522 // Untiling, so just restore geometry, and we're done.
3523 if (geometryRestore().isValid()) { // invalid if we started maximized and wait for placement
3524 moveResize(geometryRestore());
3525 }
3526 checkWorkspacePosition(); // Just in case it's a different screen
3527 } else if (mode == QuickTileMode(QuickTileFlag::Custom)) {
3528 Tile *tile = nullptr;
3529 if (keyboard) {
3530 tile = workspace()->tileManager(output())->bestTileForPosition(moveResizeGeometry().center());
3531 } else {
3532 Output *output = workspace()->outputAt(Cursors::self()->mouse()->pos());
3533 tile = workspace()->tileManager(output)->bestTileForPosition(Cursors::self()->mouse()->pos());
3534 }
3535 setTile(tile);
3536 } else {
3537 // Use whichScreen to move to next screen when retiling to the same edge as the old behavior
3538 Output *output = workspace()->outputAt(whichScreen);
3539 Tile *tile = workspace()->tileManager(output)->quickTile(mode);
3540 setTile(tile);
3541 }
3542
3543 doSetQuickTileMode();
3544 Q_EMIT quickTileModeChanged();
3545}
3546
3547void Window::setTile(Tile *tile)
3548{
3549 if (m_tile == tile) {
3550 return;
3551 } else if (m_tile) {
3552 m_tile->removeWindow(this);
3553 }
3554
3555 m_tile = tile;
3556
3557 if (m_tile) {
3558 m_tile->addWindow(this);
3559 }
3560
3561 Q_EMIT tileChanged(tile);
3562}
3563
3564Tile *Window::tile() const
3565{
3566 return m_tile;
3567}
3568
3569void Window::doSetQuickTileMode()
3570{
3571}
3572
3573void Window::doSetHidden()
3574{
3575}
3576
3577void Window::doSetHiddenByShowDesktop()
3578{
3579}
3580
3581QRectF Window::moveToArea(const QRectF &geometry, const QRectF &oldArea, const QRectF &newArea)
3582{
3583 QRectF ret = geometry;
3584 // move the window to have the same relative position to the center of the screen
3585 // (i.e. one near the middle of the right edge will also end up near the middle of the right edge)
3586 QPointF center = geometry.center() - oldArea.center();
3587 center.setX(center.x() * newArea.width() / oldArea.width());
3588 center.setY(center.y() * newArea.height() / oldArea.height());
3589 center += newArea.center();
3590 ret.moveCenter(center);
3591
3592 // If the window was inside the old screen area, explicitly make sure its inside also the new screen area
3593 if (oldArea.contains(geometry)) {
3594 ret = keepInArea(ret, newArea);
3595 }
3596 return ret;
3597}
3598
3599QRectF Window::ensureSpecialStateGeometry(const QRectF &geometry)
3600{
3601 if (isRequestedFullScreen()) {
3602 return workspace()->clientArea(FullScreenArea, this, geometry.center());
3603 } else if (requestedMaximizeMode() != MaximizeRestore) {
3604 const QRectF maximizeArea = workspace()->clientArea(MaximizeArea, this, geometry.center());
3605 QRectF ret = geometry;
3606 if (requestedMaximizeMode() & MaximizeHorizontal) {
3607 ret.setX(maximizeArea.x());
3608 ret.setWidth(maximizeArea.width());
3609 }
3610 if (requestedMaximizeMode() & MaximizeVertical) {
3611 ret.setY(maximizeArea.y());
3612 ret.setHeight(maximizeArea.height());
3613 }
3614 return ret;
3615 } else if (quickTileMode() != QuickTileMode(QuickTileFlag::None)) {
3616 return quickTileGeometry(quickTileMode(), geometry.center());
3617 } else {
3618 return geometry;
3619 }
3620}
3621
3622void Window::sendToOutput(Output *newOutput)
3623{
3624 newOutput = rules()->checkOutput(newOutput);
3625 if (isActive()) {
3626 workspace()->setActiveOutput(newOutput);
3627 // might impact the layer of a fullscreen window
3628 const auto windows = workspace()->windows();
3629 for (Window *other : windows) {
3630 if (other->isFullScreen() && other->output() == newOutput) {
3631 other->updateLayer();
3632 }
3633 }
3634 }
3635 if (moveResizeOutput() == newOutput) {
3636 return;
3637 }
3638
3639 const QRectF oldGeom = moveResizeGeometry();
3640 const QRectF oldScreenArea = workspace()->clientArea(MaximizeArea, this, moveResizeOutput());
3641 const QRectF screenArea = workspace()->clientArea(MaximizeArea, this, newOutput);
3642
3643 if (m_quickTileMode == QuickTileMode(QuickTileFlag::Custom)) {
3644 setTile(nullptr);
3645 }
3646
3647 QRectF newGeom = moveToArea(oldGeom, oldScreenArea, screenArea);
3648 newGeom = ensureSpecialStateGeometry(newGeom);
3649 moveResize(newGeom);
3650
3651 // move geometry restores to the new output as well
3652 m_fullscreenGeometryRestore = moveToArea(m_fullscreenGeometryRestore, oldScreenArea, screenArea);
3653 m_maximizeGeometryRestore = moveToArea(m_maximizeGeometryRestore, oldScreenArea, screenArea);
3654
3655 auto tso = workspace()->ensureStackingOrder(transients());
3656 for (auto it = tso.constBegin(), end = tso.constEnd(); it != end; ++it) {
3657 (*it)->sendToOutput(newOutput);
3658 }
3659}
3660
3661void Window::checkWorkspacePosition(QRectF oldGeometry, const VirtualDesktop *oldDesktop)
3662{
3663 if (isDeleted()) {
3664 qCWarning(KWIN_CORE) << "Window::checkWorkspacePosition: called for a closed window. Consider this a bug";
3665 return;
3666 }
3667 if (isDock() || isDesktop() || !isPlaceable()) {
3668 return;
3669 }
3670
3671 QRectF newGeom = moveResizeGeometry();
3672
3673 if (!oldGeometry.isValid()) {
3674 oldGeometry = newGeom;
3675 }
3676
3677 VirtualDesktop *desktop = !isOnCurrentDesktop() ? desktops().constLast() : VirtualDesktopManager::self()->currentDesktop();
3678 if (!oldDesktop) {
3679 oldDesktop = desktop;
3680 }
3681
3682 // If the window was touching an edge before but not now move it so it is again.
3683 // Old and new maximums have different starting values so windows on the screen
3684 // edge will move when a new strut is placed on the edge.
3685 QRect oldScreenArea;
3686 QRect screenArea;
3687 if (workspace()->inUpdateClientArea()) {
3688 // check if the window is on an about to be destroyed output
3689 Output *newOutput = moveResizeOutput();
3690 if (!workspace()->outputs().contains(newOutput)) {
3691 newOutput = workspace()->outputAt(newGeom.center());
3692 }
3693 // we need to find the screen area as it was before the change
3694 oldScreenArea = workspace()->previousScreenSizes().value(moveResizeOutput());
3695 if (oldScreenArea.isNull()) {
3696 oldScreenArea = newOutput->geometry();
3697 }
3698 screenArea = newOutput->geometry();
3699 newGeom.translate(screenArea.topLeft() - oldScreenArea.topLeft());
3700 } else {
3701 oldScreenArea = workspace()->clientArea(ScreenArea, workspace()->outputAt(oldGeometry.center()), oldDesktop).toRect();
3702 screenArea = workspace()->clientArea(ScreenArea, this, newGeom.center()).toRect();
3703 }
3704
3705 if (isRequestedFullScreen() || requestedMaximizeMode() != MaximizeRestore || quickTileMode() != QuickTileMode(QuickTileFlag::None)) {
3706 moveResize(ensureSpecialStateGeometry(newGeom));
3707 m_fullscreenGeometryRestore = moveToArea(m_fullscreenGeometryRestore, oldScreenArea, screenArea);
3708 m_maximizeGeometryRestore = moveToArea(m_maximizeGeometryRestore, oldScreenArea, screenArea);
3709 return;
3710 }
3711
3712 const QRect oldGeomTall = QRect(oldGeometry.x(), oldScreenArea.y(), oldGeometry.width(), oldScreenArea.height()); // Full screen height
3713 const QRect oldGeomWide = QRect(oldScreenArea.x(), oldGeometry.y(), oldScreenArea.width(), oldGeometry.height()); // Full screen width
3714 int oldTopMax = oldScreenArea.y();
3715 int oldRightMax = oldScreenArea.x() + oldScreenArea.width();
3716 int oldBottomMax = oldScreenArea.y() + oldScreenArea.height();
3717 int oldLeftMax = oldScreenArea.x();
3718 int topMax = screenArea.y();
3719 int rightMax = screenArea.x() + screenArea.width();
3720 int bottomMax = screenArea.y() + screenArea.height();
3721 int leftMax = screenArea.x();
3722 const QRect newGeomTall = QRect(newGeom.x(), screenArea.y(), newGeom.width(), screenArea.height()); // Full screen height
3723 const QRect newGeomWide = QRect(screenArea.x(), newGeom.y(), screenArea.width(), newGeom.height()); // Full screen width
3724 // Get the max strut point for each side where the window is (E.g. Highest point for
3725 // the bottom struts bounded by the window's left and right sides).
3726
3727 // These 4 compute old bounds ...
3728 auto moveAreaFunc = workspace()->inUpdateClientArea() ? &Workspace::previousRestrictedMoveArea : //... the restricted areas changed
3729 &Workspace::restrictedMoveArea; //... when e.g. active desktop or screen changes
3730
3731 for (const QRect &r : (workspace()->*moveAreaFunc)(oldDesktop, StrutAreaTop)) {
3732 QRect rect = r & oldGeomTall;
3733 if (!rect.isEmpty()) {
3734 oldTopMax = std::max(oldTopMax, rect.y() + rect.height());
3735 }
3736 }
3737 for (const QRect &r : (workspace()->*moveAreaFunc)(oldDesktop, StrutAreaRight)) {
3738 QRect rect = r & oldGeomWide;
3739 if (!rect.isEmpty()) {
3740 oldRightMax = std::min(oldRightMax, rect.x());
3741 }
3742 }
3743 for (const QRect &r : (workspace()->*moveAreaFunc)(oldDesktop, StrutAreaBottom)) {
3744 QRect rect = r & oldGeomTall;
3745 if (!rect.isEmpty()) {
3746 oldBottomMax = std::min(oldBottomMax, rect.y());
3747 }
3748 }
3749 for (const QRect &r : (workspace()->*moveAreaFunc)(oldDesktop, StrutAreaLeft)) {
3750 QRect rect = r & oldGeomWide;
3751 if (!rect.isEmpty()) {
3752 oldLeftMax = std::max(oldLeftMax, rect.x() + rect.width());
3753 }
3754 }
3755
3756 // These 4 compute new bounds
3757 for (const QRect &r : workspace()->restrictedMoveArea(desktop, StrutAreaTop)) {
3758 QRect rect = r & newGeomTall;
3759 if (!rect.isEmpty()) {
3760 topMax = std::max(topMax, rect.y() + rect.height());
3761 }
3762 }
3763 for (const QRect &r : workspace()->restrictedMoveArea(desktop, StrutAreaRight)) {
3764 QRect rect = r & newGeomWide;
3765 if (!rect.isEmpty()) {
3766 rightMax = std::min(rightMax, rect.x());
3767 }
3768 }
3769 for (const QRect &r : workspace()->restrictedMoveArea(desktop, StrutAreaBottom)) {
3770 QRect rect = r & newGeomTall;
3771 if (!rect.isEmpty()) {
3772 bottomMax = std::min(bottomMax, rect.y());
3773 }
3774 }
3775 for (const QRect &r : workspace()->restrictedMoveArea(desktop, StrutAreaLeft)) {
3776 QRect rect = r & newGeomWide;
3777 if (!rect.isEmpty()) {
3778 leftMax = std::max(leftMax, rect.x() + rect.width());
3779 }
3780 }
3781
3782 // Check if the sides were inside or touching but are no longer
3783 enum {
3784 Left = 0,
3785 Top,
3786 Right,
3787 Bottom,
3788 };
3789 bool keep[4] = {false, false, false, false};
3790 bool save[4] = {false, false, false, false};
3791 if (oldGeometry.x() >= oldLeftMax) {
3792 save[Left] = newGeom.x() < leftMax;
3793 }
3794 if (oldGeometry.x() == oldLeftMax) {
3795 keep[Left] = newGeom.x() != leftMax;
3796 }
3797
3798 if (oldGeometry.y() >= oldTopMax) {
3799 save[Top] = newGeom.y() < topMax;
3800 }
3801 if (oldGeometry.y() == oldTopMax) {
3802 keep[Top] = newGeom.y() != topMax;
3803 }
3804
3805 if (oldGeometry.right() <= oldRightMax) {
3806 save[Right] = newGeom.right() > rightMax;
3807 }
3808 if (oldGeometry.right() == oldRightMax) {
3809 keep[Right] = newGeom.right() != rightMax;
3810 }
3811
3812 if (oldGeometry.bottom() <= oldBottomMax) {
3813 save[Bottom] = newGeom.bottom() > bottomMax;
3814 }
3815 if (oldGeometry.bottom() == oldBottomMax) {
3816 keep[Bottom] = newGeom.bottom() != bottomMax;
3817 }
3818
3819 // if randomly touches opposing edges, do not favor either
3820 if (keep[Left] && keep[Right]) {
3821 keep[Left] = keep[Right] = false;
3822 }
3823 if (keep[Top] && keep[Bottom]) {
3824 keep[Top] = keep[Bottom] = false;
3825 }
3826
3827 if (save[Left] || keep[Left]) {
3828 newGeom.moveLeft(std::max(leftMax, screenArea.x()));
3829 }
3830 if (save[Top] || keep[Top]) {
3831 newGeom.moveTop(std::max(topMax, screenArea.y()));
3832 }
3833 if (save[Right] || keep[Right]) {
3834 newGeom.moveRight(std::min(rightMax, screenArea.right()) + 1);
3835 }
3836 if (save[Bottom] || keep[Bottom]) {
3837 newGeom.moveBottom(std::min(bottomMax, screenArea.bottom()) + 1);
3838 }
3839
3840 if (oldGeometry.x() >= oldLeftMax && newGeom.x() < leftMax) {
3841 newGeom.setLeft(std::max(leftMax, screenArea.x()));
3842 }
3843 if (oldGeometry.y() >= oldTopMax && newGeom.y() < topMax) {
3844 newGeom.setTop(std::max(topMax, screenArea.y()));
3845 }
3846
3847 checkOffscreenPosition(&newGeom, screenArea);
3848 // Obey size hints. TODO: We really should make sure it stays in the right place
3849 if (!isShade()) {
3850 newGeom.setSize(constrainFrameSize(newGeom.size()));
3851 }
3852
3853 moveResize(newGeom);
3854}
3855
3856void Window::checkOffscreenPosition(QRectF *geom, const QRectF &screenArea)
3857{
3858 if (geom->left() > screenArea.right()) {
3859 geom->moveLeft(screenArea.right() - screenArea.width() / 4);
3860 } else if (geom->right() < screenArea.left()) {
3861 geom->moveRight(screenArea.left() + screenArea.width() / 4);
3862 }
3863 if (geom->top() > screenArea.bottom()) {
3864 geom->moveTop(screenArea.bottom() - screenArea.height() / 4);
3865 } else if (geom->bottom() < screenArea.top()) {
3866 geom->moveBottom(screenArea.top() + screenArea.width() / 4);
3867 }
3868}
3869
3875QSizeF Window::constrainClientSize(const QSizeF &size, SizeMode mode) const
3876{
3877 qreal width = size.width();
3878 qreal height = size.height();
3879
3880 // When user is resizing the window, the move resize geometry may have negative width or
3881 // height. In which case, we need to set negative dimensions to reasonable values.
3882 if (width < 1) {
3883 width = 1;
3884 }
3885 if (height < 1) {
3886 height = 1;
3887 }
3888
3889 const QSizeF minimumSize = minSize();
3890 const QSizeF maximumSize = maxSize();
3891
3892 width = std::clamp(width, minimumSize.width(), maximumSize.width());
3893 height = std::clamp(height, minimumSize.height(), maximumSize.height());
3894
3895 return QSizeF(width, height);
3896}
3897
3901QSizeF Window::constrainFrameSize(const QSizeF &size, SizeMode mode) const
3902{
3903 const QSizeF unconstrainedClientSize = frameSizeToClientSize(size);
3904 const QSizeF constrainedClientSize = constrainClientSize(unconstrainedClientSize, mode);
3905 return clientSizeToFrameSize(constrainedClientSize);
3906}
3907
3908QRectF Window::fullscreenGeometryRestore() const
3909{
3910 return m_fullscreenGeometryRestore;
3911}
3912
3913void Window::setFullscreenGeometryRestore(const QRectF &geom)
3914{
3915 m_fullscreenGeometryRestore = geom;
3916}
3917
3923bool Window::isFullScreenable() const
3924{
3925 return false;
3926}
3927
3935bool Window::isFullScreen() const
3936{
3937 return false;
3938}
3939
3940bool Window::isRequestedFullScreen() const
3941{
3942 return isFullScreen();
3943}
3944
3952void Window::setFullScreen(bool set)
3953{
3954 qCWarning(KWIN_CORE, "%s doesn't support setting fullscreen state", metaObject()->className());
3955}
3956
3957bool Window::wantsAdaptiveSync() const
3958{
3959 return rules()->checkAdaptiveSync(isFullScreen());
3960}
3961
3967bool Window::isMinimizable() const
3968{
3969 return false;
3970}
3971
3977bool Window::isMaximizable() const
3978{
3979 return false;
3980}
3981
3987MaximizeMode Window::maximizeMode() const
3988{
3989 return MaximizeRestore;
3990}
3991
3999MaximizeMode Window::requestedMaximizeMode() const
4000{
4001 return maximizeMode();
4002}
4003
4007QRectF Window::geometryRestore() const
4008{
4009 return m_maximizeGeometryRestore;
4010}
4011
4015void Window::setGeometryRestore(const QRectF &rect)
4016{
4017 m_maximizeGeometryRestore = rect;
4018}
4019
4020void Window::invalidateDecoration()
4021{
4022}
4023
4024bool Window::noBorder() const
4025{
4026 return true;
4027}
4028
4029bool Window::userCanSetNoBorder() const
4030{
4031 return false;
4032}
4033
4034void Window::setNoBorder(bool set)
4035{
4036 qCWarning(KWIN_CORE, "%s doesn't support setting decorations", metaObject()->className());
4037}
4038
4039void Window::checkNoBorder()
4040{
4041 setNoBorder(false);
4042}
4043
4044void Window::showOnScreenEdge()
4045{
4046 qCWarning(KWIN_CORE, "%s doesn't support screen edge activation", metaObject()->className());
4047}
4048
4049bool Window::isPlaceable() const
4050{
4051 return true;
4052}
4053
4054void Window::cleanTabBox()
4055{
4056#if KWIN_BUILD_TABBOX
4057 TabBox::TabBox *tabBox = workspace()->tabbox();
4058 if (tabBox && tabBox->isDisplayed() && tabBox->currentClient() == this) {
4059 tabBox->nextPrev(true);
4060 }
4061#endif
4062}
4063
4064bool Window::supportsWindowRules() const
4065{
4066 return false;
4067}
4068
4069void Window::removeRule(Rules *rule)
4070{
4071 m_rules.remove(rule);
4072}
4073
4074void Window::evaluateWindowRules()
4075{
4076 setupWindowRules();
4077 applyWindowRules();
4078}
4079
4080void Window::setupWindowRules()
4081{
4082 disconnect(this, &Window::captionNormalChanged, this, &Window::evaluateWindowRules);
4083 m_rules = workspace()->rulebook()->find(this);
4084 // check only after getting the rules, because there may be a rule forcing window type
4085}
4086
4087void Window::updateWindowRules(Rules::Types selection)
4088{
4089 if (workspace()->rulebook()->areUpdatesDisabled()) {
4090 return;
4091 }
4092 m_rules.update(this, selection);
4093}
4094
4095void Window::finishWindowRules()
4096{
4097 disconnect(this, &Window::captionNormalChanged, this, &Window::evaluateWindowRules);
4098 updateWindowRules(Rules::All);
4099 m_rules = WindowRules();
4100}
4101
4102// Applies Force, ForceTemporarily and ApplyNow rules
4103// Used e.g. after the rules have been modified using the kcm.
4104void Window::applyWindowRules()
4105{
4106 Q_ASSERT(!isDeleted());
4107 // apply force rules
4108 // Placement - does need explicit update, just like some others below
4109 // Geometry : setGeometry() doesn't check rules
4110 auto client_rules = rules();
4111 const QRectF oldGeometry = moveResizeGeometry();
4112 const QRectF geometry = client_rules->checkGeometrySafe(oldGeometry);
4113 if (geometry != oldGeometry) {
4114 moveResize(geometry);
4115 }
4116 // MinSize, MaxSize handled by Geometry
4117 // IgnoreGeometry
4118 setDesktops(desktops());
4119 workspace()->sendWindowToOutput(this, moveResizeOutput());
4120 setOnActivities(activities());
4121 // Type
4122 maximize(requestedMaximizeMode());
4123 setMinimized(isMinimized());
4124 setShade(shadeMode());
4125 setOriginalSkipTaskbar(skipTaskbar());
4126 setSkipPager(skipPager());
4127 setSkipSwitcher(skipSwitcher());
4128 setKeepAbove(keepAbove());
4129 setKeepBelow(keepBelow());
4130 setFullScreen(isRequestedFullScreen());
4131 setNoBorder(noBorder());
4132 updateColorScheme();
4133 updateLayer();
4134 // FSP
4135 // AcceptFocus :
4136 if (workspace()->mostRecentlyActivatedWindow() == this
4137 && !client_rules->checkAcceptFocus(true)) {
4139 }
4140 // Autogrouping : Only checked on window manage
4141 // AutogroupInForeground : Only checked on window manage
4142 // AutogroupById : Only checked on window manage
4143 // StrictGeometry
4144 setShortcut(rules()->checkShortcut(shortcut().toString()));
4145 // see also X11Window::setActive()
4146 if (isActive()) {
4147 setOpacity(rules()->checkOpacityActive(qRound(opacity() * 100.0)) / 100.0);
4148 workspace()->disableGlobalShortcutsForClient(rules()->checkDisableGlobalShortcuts(false));
4149 } else {
4150 setOpacity(rules()->checkOpacityInactive(qRound(opacity() * 100.0)) / 100.0);
4151 }
4152 setDesktopFileName(rules()->checkDesktopFile(desktopFileName()));
4153}
4154
4155void Window::setLastUsageSerial(quint32 serial)
4156{
4157 if (m_lastUsageSerial < serial) {
4158 m_lastUsageSerial = serial;
4159 }
4160}
4161
4162quint32 Window::lastUsageSerial() const
4163{
4164 return m_lastUsageSerial;
4165}
4166
4167uint32_t Window::interactiveMoveResizeCount() const
4168{
4169 return m_interactiveMoveResize.counter;
4170}
4171
4172void Window::setLockScreenOverlay(bool allowed)
4173{
4174 if (m_lockScreenOverlay == allowed) {
4175 return;
4176 }
4177 m_lockScreenOverlay = allowed;
4178 Q_EMIT lockScreenOverlayChanged();
4179}
4180
4181bool Window::isLockScreenOverlay() const
4182{
4183 return m_lockScreenOverlay;
4184}
4185
4186void Window::refOffscreenRendering()
4187{
4188 if (m_offscreenRenderCount == 0) {
4189 m_offscreenFramecallbackTimer.start(1'000'000 / output()->refreshRate());
4190 }
4191 m_offscreenRenderCount++;
4192}
4193
4194void Window::unrefOffscreenRendering()
4195{
4196 Q_ASSERT(m_offscreenRenderCount);
4197 m_offscreenRenderCount--;
4198 if (m_offscreenRenderCount == 0) {
4199 m_offscreenFramecallbackTimer.stop();
4200 }
4201}
4202
4203void Window::maybeSendFrameCallback()
4204{
4205 if (m_surface && !m_windowItem->isVisible()) {
4206 const auto timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
4207 m_surface->traverseTree([this, &timestamp](SurfaceInterface *surface) {
4208 surface->frameRendered(timestamp);
4209 const auto feedback = surface->takePresentationFeedback(nullptr);
4210 if (feedback) {
4211 feedback->presented(std::chrono::nanoseconds(1'000'000'000'000 / output()->refreshRate()), std::chrono::steady_clock::now().time_since_epoch(), PresentationMode::VSync);
4212 }
4213 });
4214 // update refresh rate, it might have changed
4215 m_offscreenFramecallbackTimer.start(1'000'000 / output()->refreshRate());
4216 }
4217}
4218
4219WindowOffscreenRenderRef::WindowOffscreenRenderRef(Window *window)
4220 : m_window(window)
4221{
4222 window->refOffscreenRendering();
4223}
4224
4226{
4227 if (m_window) {
4228 m_window->unrefOffscreenRendering();
4229 }
4230}
4231
4233{
4234 return !isDeleted() && !isHidden() && !isHiddenByShowDesktop() && !isMinimized();
4235}
4236
4238{
4239 return m_hidden;
4240}
4241
4242void Window::setHidden(bool hidden)
4243{
4244 if (m_hidden == hidden) {
4245 return;
4246 }
4247 m_hidden = hidden;
4248 doSetHidden();
4249 if (hidden) {
4250 workspace()->windowHidden(this);
4251 Q_EMIT windowHidden(this);
4252 } else {
4253 Q_EMIT windowShown(this);
4254 }
4255}
4256
4258{
4259 return m_hiddenByShowDesktop;
4260}
4261
4270
4272{
4273 return m_suspended;
4274}
4275
4276void Window::setSuspended(bool suspended)
4277{
4278 if (isDeleted()) {
4279 return;
4280 }
4281 if (m_suspended != suspended) {
4282 m_suspended = suspended;
4284 }
4285}
4286
4288{
4289}
4290
4291} // namespace KWin
4292
4293#include "moc_window.cpp"
@ OperationModeX11
KWin uses only X11 for managing windows and compositing.
Definition main.h:87
void applicationMenuEnabledChanged(bool enabled)
const QString & hostName() const
static QString localhost()
WorkspaceScene * scene() const
Definition compositor.h:60
static Compositor * self()
Wrapper round Qt::CursorShape with extensions enums into a single entity.
Definition cursor.h:50
Representation of a window used by/for Effect classes.
void update(Window *window, Change change)
Updates the position of the window according to the requested change in the focus chain.
void positionChanged()
void boundingRectChanged()
MouseCommand commandWindowWheel
Definition options.h:151
KWin::Options::WindowOperation operationTitlebarDblClick
Definition options.h:138
bool isShadeHover() const
Definition options.h:302
MouseCommand commandInactiveTitlebar1
Definition options.h:145
bool isAutoRaise() const
Definition options.h:278
MouseCommand commandInactiveTitlebar2
Definition options.h:146
bool isClickRaise() const
Definition options.h:270
MouseCommand commandActiveTitlebar2
Definition options.h:143
bool electricBorderTiling
Definition options.h:167
MouseCommand commandWindow2
Definition options.h:149
FocusPolicy focusPolicy
Definition options.h:77
MouseCommand commandActiveTitlebar3
Definition options.h:144
MouseCommand commandActiveTitlebar1
Definition options.h:142
MouseCommand commandInactiveTitlebar3
Definition options.h:147
int shadeHoverInterval
Definition options.h:105
MouseCommand commandWindow3
Definition options.h:150
float electricBorderCornerRatio
Definition options.h:171
bool focusPolicyIsReasonable
Definition options.h:113
bool electricBorderMaximize
Definition options.h:163
bool isSeparateScreenFocus() const
Definition options.h:318
int autoRaiseInterval
Definition options.h:93
MouseCommand commandWindow1
Definition options.h:148
void hide()
Definition outline.cpp:52
void show()
Definition outline.cpp:38
QRect geometry
Definition output.h:134
Output * handle() const
Definition output.cpp:263
void place(Window *c, const QRectF &area)
Definition placement.cpp:40
void removePlasmaVirtualDesktop(const QString &id)
void setTitle(const QString &title)
void addPlasmaVirtualDesktop(const QString &id)
PlasmaWindowInterface * createWindow(QObject *parent, const QUuid &uuid)
WindowRules find(const Window *window) const
Definition rules.cpp:877
@ SkipTaskbar
Definition rules.h:103
@ SkipPager
Definition rules.h:104
@ Desktops
Definition rules.h:98
@ SkipSwitcher
Definition rules.h:105
void reserveDesktopSwitching(bool isToReserve, Qt::Orientations o)
Class representing a Window's Shadow to be rendered by the Compositor.
Definition shadow.h:48
static std::unique_ptr< Shadow > createShadow(Window *window)
Definition shadow.cpp:42
Resource representing a wl_surface.
Definition surface.h:80
std::unique_ptr< PresentationFeedback > takePresentationFeedback(Output *output)
Definition surface.cpp:471
void frameRendered(quint32 msec)
Definition surface.cpp:459
QModelIndex nextPrev(bool forward) const
void addWindow(Window *window)
Definition tile.cpp:282
QRectF windowGeometry() const
Definition tile.cpp:139
KWin::Tile * quickTile(QuickTileMode mode) const
KWin::Tile * bestTileForPosition(const QPointF &pos)
PlasmaWindowManagementInterface * windowManagement() const
Window * findWindow(const SurfaceInterface *surface) const
std::unique_ptr< Shadow > m_shadow
Definition window.h:1735
virtual bool isLocalhost() const
Definition window.cpp:409
void interactiveMoveResizeStarted()
bool m_active
Definition window.h:1752
QMatrix4x4 inputTransformation() const
Definition window.cpp:374
void setOpacity(qreal opacity)
Definition window.cpp:196
bool m_skipTaskbar
Definition window.h:1744
virtual QRectF resizeWithChecks(const QRectF &geometry, const QSizeF &s)=0
void updateElectricGeometryRestore()
Definition window.cpp:3382
QTimer m_offscreenFramecallbackTimer
Definition window.h:1839
void startDelayedInteractiveMoveResize()
Definition window.cpp:1356
void maximizedChanged()
QPointF pos
Definition window.h:79
int borderRight() const
Definition window.cpp:2366
bool isActiveFullScreen() const
Definition window.cpp:2343
void skipSwitcherChanged()
void triggerDecorationRepaint()
Definition window.cpp:2683
virtual bool hasTransientPlacementHint() const
Definition window.cpp:2240
bool skipSwitcher
Definition window.h:320
WindowItem * windowItem() const
Definition window.h:2027
void setMinimized(bool set)
Definition window.cpp:984
void stackingOrderChanged()
bool m_hidden
Definition window.h:1729
virtual void doSetSuspended()
Definition window.cpp:4287
QRectF frameGeometry
Definition window.h:431
bool m_originalSkipTaskbar
Definition window.h:1748
void invalidateDecorationDoubleClickTimer()
Definition window.cpp:2797
void setSurface(SurfaceInterface *surface)
Definition window.cpp:347
void inputTransformationChanged()
virtual void doSetSkipSwitcher()
Definition window.cpp:489
QList< VirtualDesktop * > m_desktops
Definition window.h:1762
virtual QList< Window * > mainWindows() const
Definition window.cpp:2256
void refOffscreenRendering()
Definition window.cpp:4186
QRectF m_bufferGeometry
Definition window.h:1727
bool isInteractiveMoveResizePointerButtonDown() const
Definition window.h:1624
virtual void doSetDesktop()
Definition window.cpp:782
void setElectricBorderMode(QuickTileMode mode)
Definition window.cpp:3329
QPointF mapToLocal(const QPointF &point) const
Definition window.cpp:399
virtual bool isClient() const
Definition window.cpp:302
QString wmClientMachine(bool use_localhost) const
Definition window.cpp:171
QPointer< Tile > m_tile
Definition window.h:1779
virtual void updateWindowRules(Rules::Types selection)
Definition window.cpp:4087
bool m_keepAbove
Definition window.h:1754
bool isInteractiveMove() const
Definition window.h:1107
virtual void doSetKeepAbove()
Definition window.cpp:645
void skipCloseAnimationChanged()
void iconChanged()
QRectF bufferGeometry
Definition window.h:69
MoveResizeMode m_pendingMoveResizeMode
Definition window.h:1791
void moveResize(const QRectF &rect)
Definition window.cpp:3323
void setShade(bool set)
Definition window.cpp:863
virtual void updateMouseGrab()
Definition window.cpp:422
bool m_suspended
Definition window.h:1758
void keepInArea(QRectF area, bool partial=false)
Definition window.cpp:1129
bool isSuspended() const
Definition window.cpp:4271
virtual QString preferredColorScheme() const
Definition window.cpp:1018
QRectF initialInteractiveMoveResizeGeometry() const
Definition window.h:1611
qreal width
Definition window.h:99
void cancelAutoRaise()
Definition window.cpp:679
void stopDelayedInteractiveMoveResize()
Definition window.cpp:1372
bool isNotification() const
Definition window.h:1977
virtual void doSetHiddenByShowDesktop()
Definition window.cpp:3577
bool isDialog() const
Definition window.h:1952
virtual bool isMinimizable() const
Definition window.cpp:3967
bool startInteractiveMoveResize()
Definition window.cpp:1185
void skipPagerChanged()
bool isUnrestrictedInteractiveMoveResize() const
Definition window.h:1584
void setInteractiveMoveResize(bool enabled)
Definition window.h:1577
bool m_skipCloseAnimation
Definition window.h:1739
QSizeF size
Definition window.h:84
EffectWindow * effectWindow()
Definition window.cpp:279
bool isShade() const
Definition window.h:1034
SurfaceInterface * surface() const
Definition window.cpp:342
void setSkipTaskbar(bool set)
Definition window.cpp:464
void maybeSendFrameCallback()
Definition window.cpp:4203
bool isMostRecentlyRaised() const
Definition window.cpp:691
std::unique_ptr< WindowItem > m_windowItem
Definition window.h:1734
bool skipsCloseAnimation
Definition window.h:242
bool isShown() const
Definition window.cpp:4232
void fullScreenChanged()
bool keepBelow
Definition window.h:337
bool wantsTabFocus() const
Definition window.cpp:697
void shadeHover()
Definition window.cpp:908
Shadow * shadow() const
Definition window.cpp:259
void demandsAttentionChanged()
void readyForPaintingChanged()
bool isHidden() const
Definition window.cpp:4237
bool isNormalWindow() const
Definition window.h:1957
void setTransientFor(Window *transientFor)
Definition window.cpp:2217
virtual void maximize(MaximizeMode mode)
Definition window.cpp:1167
void setGeometryRestore(const QRectF &rect)
Definition window.cpp:4015
virtual bool groupTransient() const
Definition window.cpp:3233
void setColorScheme(const QString &colorScheme)
Definition window.cpp:1028
QPointF mapToFrame(const QPointF &point) const
Definition window.cpp:394
bool isOnScreenDisplay() const
Definition window.h:1992
void setOutput(Output *output)
Definition window.cpp:241
void toggleShade()
Definition window.cpp:947
bool isInteractiveMoveResize() const
Definition window.h:1570
void windowShown(KWin::Window *window)
~Window() override
Definition window.cpp:105
SurfaceItem * surfaceItem() const
Definition window.cpp:289
static std::shared_ptr< Decoration::DecorationPalette > s_defaultPalette
Definition window.h:1771
void unref()
Definition window.cpp:118
PlasmaWindowInterface * windowManagementInterface() const
Definition window.h:1307
static bool belongToSameApplication(const Window *c1, const Window *c2, SameApplicationChecks checks=SameApplicationChecks())
Definition window.cpp:426
const Decoration::DecorationPalette * decorationPalette()
Definition window.cpp:1012
void demandAttention(bool set=true)
Definition window.cpp:708
std::shared_ptr< KDecoration2::Decoration > decoration
Definition window.h:1820
void setKeepBelow(bool)
Definition window.cpp:649
bool isHiddenByShowDesktop() const
Definition window.cpp:4257
virtual bool isPopupWindow() const
Definition window.h:2074
void surfaceChanged()
void setActive(bool)
Definition window.cpp:499
void ensurePalette()
Definition window.cpp:1058
void opacityChanged(KWin::Window *window, qreal oldOpacity)
void setOnAllDesktops(bool set)
Definition window.cpp:813
virtual bool isFullScreen() const
Definition window.cpp:3935
bool isCriticalNotification() const
Definition window.h:1982
QuickTileMode electricBorderMode() const
Definition window.h:1527
virtual bool isMovableAcrossScreens() const =0
bool isOnAllDesktops() const
Definition window.h:2032
void handlePaletteChange()
Definition window.cpp:1091
void shadeChanged()
QRectF visibleGeometry() const
Definition window.cpp:159
virtual bool isInternal() const
Definition window.h:2022
bool isInteractiveResize() const
Definition window.h:1114
void setQuickTileMode(QuickTileMode mode, bool keyboard=false)
Definition window.cpp:3411
virtual bool isUnmanaged() const
Definition window.cpp:307
void interactiveMoveResizeFinished()
void finishInteractiveMoveResize(bool cancel)
Definition window.cpp:1262
void leaveDesktop(VirtualDesktop *desktop)
Definition window.cpp:796
bool isOnCurrentDesktop() const
Definition window.cpp:848
void setDesktops(QList< VirtualDesktop * > desktops)
Definition window.cpp:726
bool isAppletPopup() const
Definition window.h:1987
void hasApplicationMenuChanged(bool)
virtual MaximizeMode requestedMaximizeMode() const
Definition window.cpp:3999
QPalette palette()
Definition window.cpp:1006
void setOriginalSkipTaskbar(bool set)
Definition window.cpp:479
void shadeUnhover()
Definition window.cpp:914
virtual Layer belongsToLayer() const
Definition window.cpp:575
void colorSchemeChanged()
QTimer * m_shadeHoverTimer
Definition window.h:1760
void elevate(bool elevate)
Definition window.cpp:312
QuickTileMode quickTileMode() const
Definition window.h:1096
void setUnrestrictedInteractiveMoveResize(bool set)
Definition window.h:1591
void keepAboveChanged(bool)
bool wantsInput
Definition window.h:409
void startShadeHoverTimer()
Definition window.cpp:920
int m_stackingOrder
Definition window.h:1742
QIcon icon
Definition window.h:327
bool isLockScreenOverlay() const
Definition window.cpp:4181
virtual void doMinimize()
Definition window.cpp:1002
Layer m_layer
Definition window.h:1778
void setResourceClass(const QString &name, const QString &className=QString())
Definition window.cpp:184
void outputChanged()
void checkUnrestrictedInteractiveMoveResize()
Definition window.cpp:1303
int borderLeft() const
Definition window.cpp:2361
bool isMinimized() const
Definition window.h:988
QPointer< SurfaceInterface > m_surface
Definition window.h:1740
bool isModal() const
Definition window.cpp:2285
virtual bool belongsToSameApplication(const Window *other, SameApplicationChecks checks) const =0
void desktopsChanged()
void setHidden(bool hidden)
Definition window.cpp:4242
QString resourceClass
Definition window.h:115
qreal y
Definition window.h:94
bool m_skipSwitcher
Definition window.h:1750
bool isActive() const
Definition window.h:882
struct KWin::Window::@32 m_interactiveMoveResize
bool isDecorated() const
Definition window.h:1162
bool isDesktop() const
Definition window.h:1922
Output * m_output
Definition window.h:1724
QPointF mapFromLocal(const QPointF &point) const
Definition window.cpp:404
virtual void doSetSkipPager()
Definition window.cpp:460
void setSuspended(bool suspended)
Definition window.cpp:4276
virtual bool isLockScreen() const
Definition window.h:2007
qreal x
Definition window.h:89
QList< KWin::VirtualDesktop * > desktops
Definition window.h:295
bool m_deleted
Definition window.h:1753
bool isToolbar() const
Definition window.h:1937
virtual bool setupCompositing()
Definition window.cpp:207
void skipTaskbarChanged()
QString resourceName
Definition window.h:114
void moveResizedChanged()
Output * interactiveMoveResizeStartOutput() const
Definition window.h:1632
QMargins frameMargins() const
Definition window.cpp:417
QString resource_class
Definition window.h:1737
virtual bool doStartInteractiveMoveResize()
Definition window.cpp:2436
void paletteChanged(const QPalette &p)
void startShadeUnhoverTimer()
Definition window.cpp:931
void updateColorScheme()
Definition window.cpp:1053
virtual void doSetDemandsAttention()
Definition window.cpp:722
Qt::Edge titlebarPosition() const
Definition window.cpp:953
int borderTop() const
Definition window.cpp:2371
virtual void leaveInteractiveMoveResize()
Definition window.cpp:2423
void enterDesktop(VirtualDesktop *desktop)
Definition window.cpp:786
Output * moveResizeOutput() const
Definition window.cpp:3297
static QHash< QString, std::weak_ptr< Decoration::DecorationPalette > > s_palettes
Definition window.h:1770
QString caption
Definition window.h:392
virtual void doSetActive()
Definition window.cpp:536
std::shared_ptr< Decoration::DecorationPalette > m_palette
Definition window.h:1769
QString m_colorScheme
Definition window.h:1768
virtual bool isRequestedFullScreen() const
Definition window.cpp:3940
QRectF moveResizeGeometry() const
Definition window.cpp:3286
virtual std::unique_ptr< WindowItem > createItem(Scene *scene)=0
QRectF geometryRestore() const
Definition window.cpp:4007
void hiddenByShowDesktopChanged()
virtual void doSetHidden()
Definition window.cpp:3573
bool isOnDesktop(VirtualDesktop *desktop) const
Definition window.cpp:843
bool ready_for_painting
Definition window.h:1728
void bufferGeometryChanged(const QRectF &oldGeometry)
const WindowRules * rules() const
Definition window.h:1048
bool isSplash() const
Definition window.h:1942
ShadeMode shadeMode() const
Definition window.cpp:853
int m_blockGeometryUpdates
Definition window.h:1790
const QList< Window * > & transients() const
Definition window.h:2088
virtual xcb_timestamp_t userTime() const
Definition window.cpp:431
qreal height
Definition window.h:104
void setStackingOrder(int order)
Definition window.cpp:361
void minimizedChanged()
QString resource_name
Definition window.h:1736
bool hidden
Definition window.h:534
void setReadyForPainting()
Definition window.cpp:228
void setSkipPager(bool set)
Definition window.cpp:448
KWin::Window * transientFor
Definition window.h:420
virtual bool isShadeable() const
Definition window.cpp:858
int stackingOrder
Definition window.h:271
bool wantsShadowToBeRendered() const
Definition window.cpp:297
void checkWorkspacePosition(QRectF oldGeometry=QRectF(), const VirtualDesktop *oldDesktop=nullptr)
Definition window.cpp:3661
void updateCursor()
Definition window.cpp:2376
qreal opacity
Definition window.h:106
void blockGeometryUpdates()
Definition window.h:2098
qreal m_opacity
Definition window.h:1741
QStringList desktopIds() const
Definition window.cpp:830
bool isOnActiveOutput() const
Definition window.cpp:249
bool isElectricBorderMaximizing() const
Definition window.h:1532
void setHiddenByShowDesktop(bool hidden)
Definition window.cpp:4262
bool isDock() const
Definition window.h:1927
void frameGeometryChanged(const QRectF &oldGeometry)
void updateShadow()
Definition window.cpp:264
bool m_skipPager
Definition window.h:1749
virtual bool isInputMethod() const
Definition window.h:2012
void ref()
Definition window.cpp:113
void setIcon(const QIcon &icon)
Definition window.cpp:493
bool m_minimized
Definition window.h:1757
virtual void doSetSkipTaskbar()
Definition window.cpp:485
bool skipPager
Definition window.h:315
virtual void doSetKeepBelow()
Definition window.cpp:666
void activeChanged()
virtual bool hitTest(const QPointF &point) const
Definition window.cpp:381
void shadowChanged()
void keepBelowChanged(bool)
QSizeF minSize
Definition window.h:397
int borderBottom() const
Definition window.cpp:2356
bool isSpecialWindow() const
Definition window.cpp:702
void windowHidden(KWin::Window *window)
void setSkipCloseAnimation(bool set)
Definition window.cpp:333
void visibleGeometryChanged()
void cancelShadeHoverTimer()
Definition window.cpp:941
bool m_demandsAttention
Definition window.h:1756
void windowClassChanged()
virtual bool belongsToDesktop() const
Definition window.cpp:623
KWin::Layer layer
Definition window.h:529
bool m_keepBelow
Definition window.h:1755
bool keepAbove
Definition window.h:332
void hiddenChanged()
virtual void finishCompositing()
Definition window.cpp:223
void setKeepAbove(bool)
Definition window.cpp:628
struct KWin::Window::@33 m_decoration
KWin::Output * output
Definition window.h:111
bool hasApplicationMenu
Definition window.h:506
bool m_hiddenByShowDesktop
Definition window.h:1730
void updateLayer()
Definition window.cpp:560
bool titlebarPositionUnderMouse() const
Definition window.cpp:959
ClientMachine * m_clientMachine
Definition window.h:1738
virtual MaximizeMode maximizeMode() const
Definition window.cpp:3987
void setElectricBorderMaximizing(bool maximizing)
Definition window.cpp:3343
QIcon m_icon
Definition window.h:1751
ShadeMode m_shadeMode
Definition window.h:1761
void autoRaise()
Definition window.cpp:685
virtual void doSetShade(ShadeMode previousShadeMode)
Definition window.cpp:904
Gravity interactiveMoveResizeGravity() const
Definition window.h:1616
virtual void doFinishInteractiveMoveResize()
Definition window.cpp:2441
QSizeF maxSize
Definition window.h:402
void setSkipSwitcher(bool set)
Definition window.cpp:436
QRectF m_keyboardGeometryRestore
Definition window.h:1795
bool isOnOutput(Output *output) const
Definition window.cpp:254
Q_INVOKABLE void setMaximize(bool vertically, bool horizontally)
Definition window.cpp:1172
QString colorScheme
Definition window.h:527
virtual bool isPlaceable() const
Definition window.cpp:4049
QString windowRole
Definition window.h:116
QTimer * m_autoRaiseTimer
Definition window.h:1759
virtual void moveResizeInternal(const QRectF &rect, MoveResizeMode mode)=0
void startAutoRaise()
Definition window.cpp:670
bool isDeleted() const
Definition window.cpp:540
int m_refCount
Definition window.h:1732
void markAsDeleted()
Definition window.cpp:545
bool skipTaskbar
Definition window.h:310
QSizeF checkMaxSize(QSizeF s) const
Layer checkLayer(Layer layer) const
int checkOpacityInactive(int s) const
bool checkKeepBelow(bool below, bool init=false) const
bool checkSkipSwitcher(bool skip, bool init=false) const
QString checkDecoColor(QString schemeFile) const
bool checkSkipPager(bool skip, bool init=false) const
void update(Window *, int selection)
Definition rules.cpp:719
QList< VirtualDesktop * > checkDesktops(QList< VirtualDesktop * > desktops, bool init=false) const
ShadeMode checkShade(ShadeMode shade, bool init=false) const
bool checkMinimize(bool minimized, bool init=false) const
int checkOpacityActive(int s) const
bool checkSkipTaskbar(bool skip, bool init=false) const
QSizeF checkMinSize(QSizeF s) const
bool checkKeepAbove(bool above, bool init=false) const
TileManager * tileManager(Output *output)
bool inUpdateClientArea() const
void windowAttentionChanged(Window *, bool set)
Window * mostRecentlyActivatedWindow() const
Definition workspace.h:772
QRectF clientArea(clientAreaOption, const Output *output, const VirtualDesktop *desktop) const
void windowHidden(Window *)
ScreenEdges * screenEdges() const
void constrain(Window *below, Window *above)
void activateWindow(Window *window, bool force=false)
const QList< Window * > & stackingOrder() const
Definition workspace.h:788
void sendWindowToOutput(Window *window, Output *output)
void windowToNextDesktop(Window *window)
Window * windowUnderMouse(Output *output) const
void showWindowMenu(const QRect &pos, Window *cl)
Outline * outline() const
void raiseWindow(Window *window, bool nogroup=false)
Definition layers.cpp:354
Placement * placement() const
static Workspace * self()
Definition workspace.h:91
Window * topWindowOnDesktop(VirtualDesktop *desktop, Output *output=nullptr, bool unconstrained=false, bool only_normal=true) const
Definition layers.cpp:224
QRectF adjustWindowSize(Window *window, QRectF moveResizeGeom, Gravity gravity)
StrutRects restrictedMoveArea(const VirtualDesktop *desktop, StrutAreas areas=StrutAreaAll) const
FocusChain * focusChain() const
void updateFocusMousePosition(const QPointF &pos)
Definition workspace.h:820
RuleBook * rulebook() const
void setMoveResizeWindow(Window *window)
void lowerWindow(Window *window, bool nogroup=false)
Definition layers.cpp:296
QList< Output * > outputs() const
Definition workspace.h:762
void setActiveWindow(Window *window)
const QList< Window * > windows() const
Definition workspace.h:248
bool activateNextWindow(Window *window)
bool requestFocus(Window *window, bool force=false)
void setActiveOutput(Output *output)
void cancelDelayFocus()
bool takeActivity(Window *window, ActivityFlags flags)
Output * outputAt(const QPointF &pos) const
void removeDeleted(Window *)
void windowToPreviousDesktop(Window *window)
void unconstrain(Window *below, Window *above)
Output * findOutput(Output *reference, Direction direction, bool wrapAround=false) const
void addDeleted(Window *)
void raiseOrLowerWindow(Window *window)
Definition layers.cpp:279
QList< X11Window * > ensureStackingOrder(const QList< X11Window * > &windows) const
Definition layers.cpp:639
void disableGlobalShortcutsForClient(bool disable)
QHash< const Output *, QRect > previousScreenSizes() const
void requestDelayFocus(Window *)
Item * containerItem() const
TabBoxHandler * tabBox
Gravity
Definition globals.h:150
ShadeMode
Definition common.h:62
@ ShadeActivated
Definition common.h:66
@ ShadeNone
Definition common.h:63
@ ShadeHover
Definition common.h:65
@ ShadeNormal
Definition common.h:64
QDebug & operator<<(QDebug &s, const KWin::DrmConnector *obj)
@ FullScreenArea
Definition globals.h:53
@ MaximizeArea
Definition globals.h:51
@ ScreenArea
Definition globals.h:57
@ PlacementArea
Definition globals.h:49
@ FullArea
Definition globals.h:56
@ WorkArea
Definition globals.h:55
MaximizeMode
Definition common.h:74
@ 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
StrutArea
Definition common.h:35
@ StrutAreaRight
Definition common.h:38
@ StrutAreaBottom
Definition common.h:39
@ StrutAreaTop
Definition common.h:37
@ StrutAreaLeft
Definition common.h:40
WaylandServer * waylandServer()
Workspace * workspace()
Definition workspace.h:830
QList< StrutRect > StrutRects
Definition common.h:60
Options * options
Definition main.cpp:73
InputRedirection * input()
Definition input.h:549
KWIN_EXPORT QPoint flooredPoint(const QPointF &point)
Definition globals.h:262
Layer
Definition globals.h:162
@ BelowLayer
Definition globals.h:166
@ UnknownLayer
Definition globals.h:163
@ DesktopLayer
Definition globals.h:165
@ CriticalNotificationLayer
Definition globals.h:172
@ AboveLayer
Definition globals.h:168
@ ActiveLayer
Definition globals.h:170
@ OverlayLayer
Definition globals.h:174
@ PopupLayer
Definition globals.h:171
@ NotificationLayer
Definition globals.h:169
@ NormalLayer
Definition globals.h:167
@ OnScreenDisplayLayer
Definition globals.h:173