KWin
Loading...
Searching...
No Matches
xdgshellwindow.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: 2018 David Edmundson <davidedmundson@kde.org>
7 SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
8
9 SPDX-License-Identifier: GPL-2.0-or-later
10*/
11#include "xdgshellwindow.h"
12#include "core/output.h"
13#if KWIN_BUILD_ACTIVITIES
14#include "activities.h"
15#endif
17#include "killprompt.h"
18#include "placement.h"
19#include "pointer_input.h"
20#include "tablet_input.h"
21#include "touch_input.h"
23#include "virtualdesktops.h"
24#include "wayland/appmenu.h"
25#include "wayland/output.h"
26#include "wayland/plasmashell.h"
27#include "wayland/seat.h"
30#include "wayland/surface.h"
31#include "wayland/tablet_v2.h"
33#include "wayland_server.h"
34#include "workspace.h"
35
36#include <KDecoration2/DecoratedClient>
37#include <KDecoration2/Decoration>
38
39namespace KWin
40{
41
43 : WaylandWindow(shellSurface->surface())
44 , m_shellSurface(shellSurface)
45 , m_configureTimer(new QTimer(this))
46{
47 connect(shellSurface, &XdgSurfaceInterface::configureAcknowledged,
48 this, &XdgSurfaceWindow::handleConfigureAcknowledged);
49 connect(shellSurface, &XdgSurfaceInterface::resetOccurred,
51 connect(shellSurface->surface(), &SurfaceInterface::committed,
52 this, &XdgSurfaceWindow::handleCommit);
53 connect(shellSurface, &XdgSurfaceInterface::aboutToBeDestroyed,
55 connect(shellSurface->surface(), &SurfaceInterface::aboutToBeDestroyed,
57
58 // The effective window geometry is determined by two things: (a) the rectangle that bounds
59 // the main surface and all of its sub-surfaces, (b) the client-specified window geometry, if
60 // any. If the client hasn't provided the window geometry, we fallback to the bounding sub-
61 // surface rectangle. If the client has provided the window geometry, we intersect it with
62 // the bounding rectangle and that will be the effective window geometry. It's worth to point
63 // out that geometry updates do not occur that frequently, so we don't need to recompute the
64 // bounding geometry every time the client commits the surface.
65
66 SubSurfaceMonitor *treeMonitor = new SubSurfaceMonitor(surface(), this);
67
68 connect(treeMonitor, &SubSurfaceMonitor::subSurfaceAdded,
69 this, &XdgSurfaceWindow::setHaveNextWindowGeometry);
70 connect(treeMonitor, &SubSurfaceMonitor::subSurfaceRemoved,
71 this, &XdgSurfaceWindow::setHaveNextWindowGeometry);
72 connect(treeMonitor, &SubSurfaceMonitor::subSurfaceMoved,
73 this, &XdgSurfaceWindow::setHaveNextWindowGeometry);
74 connect(treeMonitor, &SubSurfaceMonitor::subSurfaceResized,
75 this, &XdgSurfaceWindow::setHaveNextWindowGeometry);
76 connect(shellSurface, &XdgSurfaceInterface::windowGeometryChanged,
77 this, &XdgSurfaceWindow::setHaveNextWindowGeometry);
79 this, &XdgSurfaceWindow::setHaveNextWindowGeometry);
80
81 // Configure events are not sent immediately, but rather scheduled to be sent when the event
82 // loop is about to be idle. By doing this, we can avoid sending configure events that do
83 // nothing, and implementation-wise, it's simpler.
84
85 m_configureTimer->setSingleShot(true);
86 connect(m_configureTimer, &QTimer::timeout, this, &XdgSurfaceWindow::sendConfigure);
87}
88
92
93NET::WindowType XdgSurfaceWindow::windowType() const
94{
95 return m_windowType;
96}
97
99{
100 return m_lastAcknowledgedConfigure.get();
101}
102
104{
105 if (!isDeleted()) {
106 m_configureTimer->start();
107 }
108}
109
111{
112 XdgSurfaceConfigure *configureEvent = sendRoleConfigure();
113
114 // The configure event inherits configure flags from the previous event.
115 if (!m_configureEvents.isEmpty()) {
116 const XdgSurfaceConfigure *previousEvent = m_configureEvents.constLast();
117 configureEvent->flags = previousEvent->flags;
118 }
119
120 configureEvent->gravity = m_nextGravity;
121 configureEvent->flags |= m_configureFlags;
122 m_configureFlags = {};
123
124 m_configureEvents.append(configureEvent);
125}
126
127void XdgSurfaceWindow::handleConfigureAcknowledged(quint32 serial)
128{
129 m_lastAcknowledgedConfigureSerial = serial;
130}
131
132void XdgSurfaceWindow::handleCommit()
133{
134 if (!surface()->buffer()) {
135 return;
136 }
137
138 if (m_lastAcknowledgedConfigureSerial.has_value()) {
139 const quint32 serial = m_lastAcknowledgedConfigureSerial.value();
140 while (!m_configureEvents.isEmpty()) {
141 if (serial < m_configureEvents.constFirst()->serial) {
142 break;
143 }
144 m_lastAcknowledgedConfigure.reset(m_configureEvents.takeFirst());
145 }
146 }
147
149 if (haveNextWindowGeometry()) {
150 handleNextWindowGeometry();
151 resetHaveNextWindowGeometry();
152 }
153
155 m_lastAcknowledgedConfigure.reset();
156 m_lastAcknowledgedConfigureSerial.reset();
157
158 markAsMapped();
159}
160
164
168
169void XdgSurfaceWindow::maybeUpdateMoveResizeGeometry(const QRectF &rect)
170{
171 // We are about to send a configure event, ignore the committed window geometry.
172 if (m_configureTimer->isActive()) {
173 return;
174 }
175
176 // If there are unacknowledged configure events that change the geometry, don't sync
177 // the move resize geometry in order to avoid rolling back to old state. When the last
178 // configure event is acknowledged, the move resize geometry will be synchronized.
179 for (int i = m_configureEvents.count() - 1; i >= 0; --i) {
180 if (m_configureEvents[i]->flags & XdgSurfaceConfigure::ConfigurePosition) {
181 return;
182 }
183 }
184
186}
187
188void XdgSurfaceWindow::handleNextWindowGeometry()
189{
190 const QRectF boundingGeometry = surface()->boundingRect();
191
192 // The effective window geometry is defined as the intersection of the window geometry
193 // and the rectangle that bounds the main surface and all of its sub-surfaces. If the
194 // client hasn't specified the window geometry, we must fallback to the bounding geometry.
195 // Note that the xdg-shell spec is not clear about when exactly we have to clamp the
196 // window geometry.
197
198 m_windowGeometry = m_shellSurface->windowGeometry();
199 if (m_windowGeometry.isValid()) {
200 m_windowGeometry &= boundingGeometry;
201 } else {
202 m_windowGeometry = boundingGeometry;
203 }
204
205 if (m_windowGeometry.isEmpty()) {
206 qCWarning(KWIN_CORE) << "Committed empty window geometry, dealing with a buggy client!";
207 }
208
209 QRectF frameGeometry(pos(), clientSizeToFrameSize(m_windowGeometry.size()));
210 if (const XdgSurfaceConfigure *configureEvent = lastAcknowledgedConfigure()) {
211 if (configureEvent->flags & XdgSurfaceConfigure::ConfigurePosition) {
212 frameGeometry = gravitateGeometry(frameGeometry, configureEvent->bounds, configureEvent->gravity);
213 }
214 }
215
217 // Both the compositor and the client can change the window geometry. If the client
218 // sets a new window geometry, the compositor's move-resize geometry will be invalid.
219 maybeUpdateMoveResizeGeometry(frameGeometry);
220 }
221
223}
224
225bool XdgSurfaceWindow::haveNextWindowGeometry() const
226{
227 return m_haveNextWindowGeometry || m_lastAcknowledgedConfigure;
228}
229
230void XdgSurfaceWindow::setHaveNextWindowGeometry()
231{
232 m_haveNextWindowGeometry = true;
233}
234
235void XdgSurfaceWindow::resetHaveNextWindowGeometry()
236{
237 m_haveNextWindowGeometry = false;
238}
239
241{
244 return;
245 }
246
248
249 if (mode != MoveResizeMode::Move) {
250 const QSizeF requestedClientSize = frameSizeToClientSize(rect.size());
251 if (requestedClientSize == clientSize()) {
253 } else {
254 m_configureFlags |= XdgSurfaceConfigure::ConfigurePosition;
256 }
257 } else {
258 // If the window is moved, cancel any queued window position updates.
259 for (XdgSurfaceConfigure *configureEvent : std::as_const(m_configureEvents)) {
260 configureEvent->flags.setFlag(XdgSurfaceConfigure::ConfigurePosition, false);
261 }
262 m_configureFlags.setFlag(XdgSurfaceConfigure::ConfigurePosition, false);
263 updateGeometry(QRectF(rect.topLeft(), size()));
264 }
265}
266
267QRectF XdgSurfaceWindow::frameRectToBufferRect(const QRectF &rect) const
268{
269 const qreal left = rect.left() + borderLeft() - m_windowGeometry.left();
270 const qreal top = rect.top() + borderTop() - m_windowGeometry.top();
271 return QRectF(QPoint(left, top), surface()->size());
272}
273
275{
277 m_plasmaShellSurface->disconnect(this);
278 }
279 m_shellSurface->disconnect(this);
280 m_shellSurface->surface()->disconnect(this);
281}
282
284{
290 }
291 m_configureTimer->stop();
292 qDeleteAll(m_configureEvents);
293 m_configureEvents.clear();
294 cleanTabBox();
295 Q_EMIT closed();
297 workspace()->rulebook()->discardUsed(this, true);
300
301 unref();
302}
303
311{
312 m_plasmaShellSurface = shellSurface;
313
314 auto updatePosition = [this, shellSurface] {
315 move(shellSurface->position());
316 };
317 auto showUnderCursor = [this] {
318 // Wait for the first commit
319 auto moveUnderCursor = [this] {
320 if (input()->hasPointer()) {
321 move(input()->globalPointer());
322 keepInArea(workspace()->clientArea(PlacementArea, this));
323 }
324 };
325 connect(this, &Window::windowShown, this, moveUnderCursor, Qt::SingleShotConnection);
326 };
327 auto updateRole = [this, shellSurface] {
328 NET::WindowType type = NET::Unknown;
329 switch (shellSurface->role()) {
331 type = NET::Desktop;
332 break;
334 type = NET::Dock;
335 break;
337 type = NET::OnScreenDisplay;
338 break;
340 type = NET::Notification;
341 break;
343 type = NET::Tooltip;
344 break;
346 type = NET::CriticalNotification;
347 break;
349 type = NET::AppletPopup;
350 break;
352 default:
353 type = NET::Normal;
354 break;
355 }
356 if (m_windowType == type) {
357 return;
358 }
360 switch (m_windowType) {
361 case NET::Desktop:
362 case NET::Dock:
363 case NET::OnScreenDisplay:
364 case NET::Notification:
365 case NET::CriticalNotification:
366 case NET::Tooltip:
367 case NET::AppletPopup:
368 setOnAllDesktops(true);
369#if KWIN_BUILD_ACTIVITIES
370 setOnAllActivities(true);
371#endif
372 break;
373 default:
374 break;
375 }
376 };
377 connect(shellSurface, &PlasmaShellSurfaceInterface::positionChanged, this, updatePosition);
378 connect(shellSurface, &PlasmaShellSurfaceInterface::openUnderCursorRequested, this, showUnderCursor);
379 connect(shellSurface, &PlasmaShellSurfaceInterface::roleChanged, this, updateRole);
380 connect(shellSurface, &PlasmaShellSurfaceInterface::panelTakesFocusChanged, this, [this] {
381 if (m_plasmaShellSurface->panelTakesFocus()) {
382 workspace()->activateWindow(this);
383 }
384 });
385 if (shellSurface->isPositionSet()) {
386 updatePosition();
387 }
388 if (shellSurface->wantsOpenUnderCursor()) {
389 showUnderCursor();
390 }
391 updateRole();
392
393 setSkipTaskbar(shellSurface->skipTaskbar());
394 connect(shellSurface, &PlasmaShellSurfaceInterface::skipTaskbarChanged, this, [this] {
395 setSkipTaskbar(m_plasmaShellSurface->skipTaskbar());
396 });
397
398 setSkipSwitcher(shellSurface->skipSwitcher());
399 connect(shellSurface, &PlasmaShellSurfaceInterface::skipSwitcherChanged, this, [this] {
400 setSkipSwitcher(m_plasmaShellSurface->skipSwitcher());
401 });
402}
403
405 : XdgSurfaceWindow(shellSurface->xdgSurface())
406 , m_shellSurface(shellSurface)
407{
408 setDesktops({VirtualDesktopManager::self()->currentDesktop()});
409#if KWIN_BUILD_ACTIVITIES
410 if (auto a = Workspace::self()->activities()) {
411 setOnActivities({a->current()});
412 }
413#endif
414 move(workspace()->activeOutput()->geometry().center());
415
417 this, &XdgToplevelWindow::handleWindowTitleChanged);
419 this, &XdgToplevelWindow::handleWindowClassChanged);
421 this, &XdgToplevelWindow::handleWindowMenuRequested);
423 this, &XdgToplevelWindow::handleMoveRequested);
425 this, &XdgToplevelWindow::handleResizeRequested);
427 this, &XdgToplevelWindow::handleMaximizeRequested);
429 this, &XdgToplevelWindow::handleUnmaximizeRequested);
431 this, &XdgToplevelWindow::handleFullscreenRequested);
433 this, &XdgToplevelWindow::handleUnfullscreenRequested);
435 this, &XdgToplevelWindow::handleMinimizeRequested);
437 this, &XdgToplevelWindow::handleTransientForChanged);
439 this, &XdgToplevelWindow::initialize);
443 this, &XdgToplevelWindow::handleMaximumSizeChanged);
445 this, &XdgToplevelWindow::handleMinimumSizeChanged);
447 this, &XdgToplevelWindow::handlePingTimeout);
449 this, &XdgToplevelWindow::handlePingDelayed);
451 this, &XdgToplevelWindow::handlePongReceived);
452
454 this, &XdgToplevelWindow::handleForeignTransientForChanged);
455}
456
458{
459 if (m_killPrompt) {
460 m_killPrompt->quit();
461 }
462}
463
465{
467
468 if (m_appMenuInterface) {
469 m_appMenuInterface->disconnect(this);
470 }
471 if (m_paletteInterface) {
472 m_paletteInterface->disconnect(this);
473 }
474 if (m_xdgDecoration) {
475 m_xdgDecoration->disconnect(this);
476 }
477 if (m_serverDecoration) {
478 m_serverDecoration->disconnect(this);
479 }
480
481 m_shellSurface->disconnect(this);
482
484 this, &XdgToplevelWindow::handleForeignTransientForChanged);
485
487}
488
490{
491 return m_shellSurface;
492}
493
495{
496 return m_maximizeMode;
497}
498
500{
501 return m_requestedMaximizeMode;
502}
503
505{
506 const int enforcedMinimum = m_nextDecoration ? 150 : 20;
507 return rules()->checkMinSize(m_shellSurface->minimumSize()).expandedTo(QSizeF(enforcedMinimum, enforcedMinimum));
508}
509
511{
512 // enforce the same minimum as for minSize, so that maxSize is always bigger than minSize
513 const int enforcedMinimum = m_nextDecoration ? 150 : 20;
514 return rules()->checkMaxSize(m_shellSurface->maximumSize()).expandedTo(QSizeF(enforcedMinimum, enforcedMinimum));
515}
516
518{
519 return m_isFullScreen;
520}
521
523{
524 return m_isRequestedFullScreen;
525}
526
528{
529 if (isRequestedFullScreen()) {
530 return false;
531 }
532 if ((isSpecialWindow() && !isSplash() && !isToolbar()) || isAppletPopup()) {
533 return false;
534 }
535 if (rules()->checkPosition(invalidPoint) != invalidPoint) {
536 return false;
537 }
538 return true;
539}
540
542{
543 if ((isSpecialWindow() && !isSplash() && !isToolbar()) || isAppletPopup()) {
544 return false;
545 }
546 if (rules()->checkPosition(invalidPoint) != invalidPoint) {
547 return false;
548 }
549 return true;
550}
551
553{
554 if (isRequestedFullScreen()) {
555 return false;
556 }
557 if (isSpecialWindow() || isSplash() || isToolbar()) {
558 return false;
559 }
560 if (rules()->checkSize(QSize()).isValid()) {
561 return false;
562 }
563 const QSizeF min = minSize();
564 const QSizeF max = maxSize();
565 return min.width() < max.width() || min.height() < max.height();
566}
567
569{
570 return rules()->checkCloseable(!isDesktop() && !isDock());
571}
572
574{
575 if (!rules()->checkFullScreen(true)) {
576 return false;
577 }
578 return !isSpecialWindow();
579}
580
582{
583 if (!isResizable() || isAppletPopup()) {
584 return false;
585 }
586 if (rules()->checkMaximize(MaximizeRestore) != MaximizeRestore || rules()->checkMaximize(MaximizeFull) != MaximizeFull) {
587 return false;
588 }
589 return true;
590}
591
593{
594 if ((isSpecialWindow() && !isTransient()) || isAppletPopup()) {
595 return false;
596 }
597 if (!rules()->checkMinimize(true)) {
598 return false;
599 }
600 return true;
601}
602
604{
606 return !m_plasmaShellSurface->isPositionSet() && !m_plasmaShellSurface->wantsOpenUnderCursor();
607 }
608 return true;
609}
610
612{
613 return m_isTransient;
614}
615
617{
618 return (m_serverDecoration || m_xdgDecoration) && !isFullScreen() && !isShade();
619}
620
622{
623 return m_userNoBorder;
624}
625
627{
628 set = rules()->checkNoBorder(set);
629 if (m_userNoBorder == set) {
630 return;
631 }
632 m_userNoBorder = set;
633 configureDecoration();
635}
636
638{
639 clearDecoration();
640 configureDecoration();
641}
642
644{
645 return true;
646}
647
649{
651 updateCapabilities();
652}
653
655{
656 if (isCloseable()) {
657 sendPing(PingReason::CloseWindow);
658 m_shellSurface->sendClose();
659 }
660}
661
663{
664 QSize framePadding(0, 0);
665 if (m_nextDecoration) {
666 framePadding.setWidth(m_nextDecoration->borderLeft() + m_nextDecoration->borderRight());
667 framePadding.setHeight(m_nextDecoration->borderTop() + m_nextDecoration->borderBottom());
668 }
669
670 QSizeF nextClientSize = moveResizeGeometry().size();
671 if (!nextClientSize.isEmpty()) {
672 nextClientSize.rwidth() -= framePadding.width();
673 nextClientSize.rheight() -= framePadding.height();
674 }
675
676 if (nextClientSize.isEmpty()) {
677 QSizeF bounds = workspace()->clientArea(PlacementArea, this, moveResizeOutput()).size();
678 bounds.rwidth() -= framePadding.width();
679 bounds.rheight() -= framePadding.height();
680 m_shellSurface->sendConfigureBounds(bounds.toSize());
681 }
682
683 const quint32 serial = m_shellSurface->sendConfigure(nextClientSize.toSize(), m_nextStates);
684
685 XdgToplevelConfigure *configureEvent = new XdgToplevelConfigure();
686 configureEvent->bounds = moveResizeGeometry();
687 configureEvent->states = m_nextStates;
688 configureEvent->decoration = m_nextDecoration;
689 configureEvent->serial = serial;
690
691 return configureEvent;
692}
693
695{
696 auto configureEvent = static_cast<XdgToplevelConfigure *>(lastAcknowledgedConfigure());
697 if (configureEvent && decoration() != configureEvent->decoration.get()) {
698 connect(configureEvent->decoration.get(), &KDecoration2::Decoration::bordersChanged, this, [this]() {
699 if (!isDeleted()) {
700 scheduleConfigure();
701 }
702 });
703
704 setDecoration(configureEvent->decoration);
705 updateShadow();
706 }
707}
708
709void XdgToplevelWindow::handleRoleCommit()
710{
711 auto configureEvent = static_cast<XdgToplevelConfigure *>(lastAcknowledgedConfigure());
712 if (configureEvent) {
713 handleStatesAcknowledged(configureEvent->states);
714 }
715}
716
717void XdgToplevelWindow::doMinimize()
718{
719 if (m_isInitialized) {
720 if (isMinimized()) {
721 workspace()->windowHidden(this);
722 }
723 }
725}
726
727void XdgToplevelWindow::doInteractiveResizeSync(const QRectF &rect)
728{
729 moveResize(rect);
730}
731
732void XdgToplevelWindow::doSetActive()
733{
734 WaylandWindow::doSetActive();
735
736 if (isActive()) {
737 m_nextStates |= XdgToplevelInterface::State::Activated;
738 } else {
739 m_nextStates &= ~XdgToplevelInterface::State::Activated;
740 }
741
742 scheduleConfigure();
743}
744
745void XdgToplevelWindow::doSetFullScreen()
746{
747 if (isRequestedFullScreen()) {
748 m_nextStates |= XdgToplevelInterface::State::FullScreen;
749 } else {
750 m_nextStates &= ~XdgToplevelInterface::State::FullScreen;
751 }
752
753 scheduleConfigure();
754}
755
756void XdgToplevelWindow::doSetMaximized()
757{
758 if (requestedMaximizeMode() & MaximizeHorizontal) {
759 m_nextStates |= XdgToplevelInterface::State::MaximizedHorizontal;
760 } else {
761 m_nextStates &= ~XdgToplevelInterface::State::MaximizedHorizontal;
762 }
763
764 if (requestedMaximizeMode() & MaximizeVertical) {
765 m_nextStates |= XdgToplevelInterface::State::MaximizedVertical;
766 } else {
767 m_nextStates &= ~XdgToplevelInterface::State::MaximizedVertical;
768 }
769
770 scheduleConfigure();
771}
772
773static Qt::Edges anchorsForQuickTileMode(QuickTileMode mode)
774{
775 if (mode == QuickTileMode(QuickTileFlag::None)) {
776 return Qt::Edges();
777 }
778
779 Qt::Edges anchors = Qt::LeftEdge | Qt::TopEdge | Qt::RightEdge | Qt::BottomEdge;
780
781 if ((mode & QuickTileFlag::Left) && !(mode & QuickTileFlag::Right)) {
782 anchors &= ~Qt::RightEdge;
783 }
784 if ((mode & QuickTileFlag::Right) && !(mode & QuickTileFlag::Left)) {
785 anchors &= ~Qt::LeftEdge;
786 }
787
788 if ((mode & QuickTileFlag::Top) && !(mode & QuickTileFlag::Bottom)) {
789 anchors &= ~Qt::BottomEdge;
790 }
791 if ((mode & QuickTileFlag::Bottom) && !(mode & QuickTileFlag::Top)) {
792 anchors &= ~Qt::TopEdge;
793 }
794
795 return anchors;
796}
797
798void XdgToplevelWindow::doSetQuickTileMode()
799{
800 const Qt::Edges anchors = anchorsForQuickTileMode(quickTileMode());
801
802 if (anchors & Qt::LeftEdge) {
803 m_nextStates |= XdgToplevelInterface::State::TiledLeft;
804 } else {
805 m_nextStates &= ~XdgToplevelInterface::State::TiledLeft;
806 }
807
808 if (anchors & Qt::RightEdge) {
809 m_nextStates |= XdgToplevelInterface::State::TiledRight;
810 } else {
811 m_nextStates &= ~XdgToplevelInterface::State::TiledRight;
812 }
813
814 if (anchors & Qt::TopEdge) {
815 m_nextStates |= XdgToplevelInterface::State::TiledTop;
816 } else {
817 m_nextStates &= ~XdgToplevelInterface::State::TiledTop;
818 }
819
820 if (anchors & Qt::BottomEdge) {
821 m_nextStates |= XdgToplevelInterface::State::TiledBottom;
822 } else {
823 m_nextStates &= ~XdgToplevelInterface::State::TiledBottom;
824 }
825
826 scheduleConfigure();
827}
828
829bool XdgToplevelWindow::doStartInteractiveMoveResize()
830{
831 if (interactiveMoveResizeGravity() != Gravity::None) {
832 m_nextGravity = interactiveMoveResizeGravity();
833 m_nextStates |= XdgToplevelInterface::State::Resizing;
834 scheduleConfigure();
835 }
836 return true;
837}
838
839void XdgToplevelWindow::doFinishInteractiveMoveResize()
840{
841 if (m_nextStates & XdgToplevelInterface::State::Resizing) {
842 m_nextStates &= ~XdgToplevelInterface::State::Resizing;
843 scheduleConfigure();
844 }
845}
846
847void XdgToplevelWindow::doSetSuspended()
848{
849 if (isSuspended()) {
850 m_nextStates |= XdgToplevelInterface::State::Suspended;
851 } else {
852 m_nextStates &= ~XdgToplevelInterface::State::Suspended;
853 }
854
855 scheduleConfigure();
856}
857
858bool XdgToplevelWindow::takeFocus()
859{
860 if (wantsInput()) {
861 sendPing(PingReason::FocusWindow);
862 setActive(true);
863 }
864 return true;
865}
866
867bool XdgToplevelWindow::wantsInput() const
868{
869 return rules()->checkAcceptFocus(acceptsFocus());
870}
871
872bool XdgToplevelWindow::dockWantsInput() const
873{
874 if (m_plasmaShellSurface) {
875 if (m_plasmaShellSurface->role() == PlasmaShellSurfaceInterface::Role::Panel) {
876 return m_plasmaShellSurface->panelTakesFocus();
877 }
878 }
879 return false;
880}
881
882bool XdgToplevelWindow::acceptsFocus() const
883{
884 if (m_plasmaShellSurface) {
885 if (m_plasmaShellSurface->role() == PlasmaShellSurfaceInterface::Role::OnScreenDisplay || m_plasmaShellSurface->role() == PlasmaShellSurfaceInterface::Role::ToolTip) {
886 return false;
887 }
888 switch (m_plasmaShellSurface->role()) {
889 case PlasmaShellSurfaceInterface::Role::Notification:
890 case PlasmaShellSurfaceInterface::Role::CriticalNotification:
891 return m_plasmaShellSurface->panelTakesFocus();
892 default:
893 break;
894 }
895 }
896 return !isDeleted() && readyForPainting();
897}
898
899void XdgToplevelWindow::handleWindowTitleChanged()
900{
901 setCaption(m_shellSurface->windowTitle());
902}
903
904void XdgToplevelWindow::handleWindowClassChanged()
905{
906 const QString applicationId = m_shellSurface->windowClass();
907 setResourceClass(resourceName(), applicationId);
908 if (shellSurface()->isConfigured()) {
909 evaluateWindowRules();
910 }
911 setDesktopFileName(applicationId);
912}
913
914void XdgToplevelWindow::handleWindowMenuRequested(SeatInterface *seat, const QPoint &surfacePos,
915 quint32 serial)
916{
917 performMouseCommand(Options::MouseOperationsMenu, mapFromLocal(surfacePos));
918}
919
920void XdgToplevelWindow::handleMoveRequested(SeatInterface *seat, quint32 serial)
921{
922 if (!seat->hasImplicitPointerGrab(serial) && !seat->hasImplicitTouchGrab(serial)
923 && !waylandServer()->tabletManagerV2()->seat(seat)->hasImplicitGrab(serial)) {
924 return;
925 }
926 if (isMovable()) {
927 QPointF cursorPos;
928 if (seat->hasImplicitPointerGrab(serial)) {
929 cursorPos = input()->pointer()->pos();
930 } else if (seat->hasImplicitTouchGrab(serial)) {
931 cursorPos = input()->touch()->position();
932 } else {
933 cursorPos = input()->tablet()->position();
934 }
935 performMouseCommand(Options::MouseMove, cursorPos);
936 } else {
937 qCDebug(KWIN_CORE) << this << "is immovable, ignoring the move request";
938 }
939}
940
941void XdgToplevelWindow::handleResizeRequested(SeatInterface *seat, XdgToplevelInterface::ResizeAnchor anchor, quint32 serial)
942{
943 if (!seat->hasImplicitPointerGrab(serial) && !seat->hasImplicitTouchGrab(serial)
944 && !waylandServer()->tabletManagerV2()->seat(seat)->hasImplicitGrab(serial)) {
945 return;
946 }
947 if (!isResizable() || isShade()) {
948 return;
949 }
950 if (isInteractiveMoveResize()) {
951 finishInteractiveMoveResize(false);
952 }
953 setInteractiveMoveResizePointerButtonDown(true);
954 QPointF cursorPos;
955 if (seat->hasImplicitPointerGrab(serial)) {
956 cursorPos = input()->pointer()->pos();
957 } else if (seat->hasImplicitTouchGrab(serial)) {
958 cursorPos = input()->touch()->position();
959 } else {
960 cursorPos = input()->tablet()->position();
961 }
962 setInteractiveMoveOffset(cursorPos - pos()); // map from global
963 setInvertedInteractiveMoveOffset(rect().bottomRight() - interactiveMoveOffset());
964 setUnrestrictedInteractiveMoveResize(false);
965 Gravity gravity;
966 switch (anchor) {
967 case XdgToplevelInterface::ResizeAnchor::TopLeft:
968 gravity = Gravity::TopLeft;
969 break;
970 case XdgToplevelInterface::ResizeAnchor::Top:
971 gravity = Gravity::Top;
972 break;
973 case XdgToplevelInterface::ResizeAnchor::TopRight:
974 gravity = Gravity::TopRight;
975 break;
976 case XdgToplevelInterface::ResizeAnchor::Right:
977 gravity = Gravity::Right;
978 break;
979 case XdgToplevelInterface::ResizeAnchor::BottomRight:
980 gravity = Gravity::BottomRight;
981 break;
982 case XdgToplevelInterface::ResizeAnchor::Bottom:
983 gravity = Gravity::Bottom;
984 break;
985 case XdgToplevelInterface::ResizeAnchor::BottomLeft:
986 gravity = Gravity::BottomLeft;
987 break;
988 case XdgToplevelInterface::ResizeAnchor::Left:
989 gravity = Gravity::Left;
990 break;
991 default:
992 gravity = Gravity::None;
993 break;
994 }
995 setInteractiveMoveResizeGravity(gravity);
996 if (!startInteractiveMoveResize()) {
997 setInteractiveMoveResizePointerButtonDown(false);
998 }
999 updateCursor();
1000}
1001
1002void XdgToplevelWindow::handleStatesAcknowledged(const XdgToplevelInterface::States &states)
1003{
1004 const XdgToplevelInterface::States delta = m_acknowledgedStates ^ states;
1005
1006 if (delta & XdgToplevelInterface::State::Maximized) {
1007 MaximizeMode maximizeMode = MaximizeRestore;
1008 if (states & XdgToplevelInterface::State::MaximizedHorizontal) {
1009 maximizeMode = MaximizeMode(maximizeMode | MaximizeHorizontal);
1010 }
1011 if (states & XdgToplevelInterface::State::MaximizedVertical) {
1012 maximizeMode = MaximizeMode(maximizeMode | MaximizeVertical);
1013 }
1014 updateMaximizeMode(maximizeMode);
1015 }
1016 if (delta & XdgToplevelInterface::State::FullScreen) {
1017 updateFullScreenMode(states & XdgToplevelInterface::State::FullScreen);
1018 }
1019
1020 m_acknowledgedStates = states;
1021}
1022
1023void XdgToplevelWindow::handleMaximizeRequested()
1024{
1025 if (m_isInitialized) {
1026 maximize(MaximizeFull);
1027 scheduleConfigure();
1028 } else {
1029 m_initialStates |= XdgToplevelInterface::State::Maximized;
1030 }
1031}
1032
1033void XdgToplevelWindow::handleUnmaximizeRequested()
1034{
1035 if (m_isInitialized) {
1036 maximize(MaximizeRestore);
1037 scheduleConfigure();
1038 } else {
1039 m_initialStates &= ~XdgToplevelInterface::State::Maximized;
1040 }
1041}
1042
1043void XdgToplevelWindow::handleFullscreenRequested(OutputInterface *output)
1044{
1045 m_fullScreenRequestedOutput = output ? output->handle() : nullptr;
1046 if (m_isInitialized) {
1047 setFullScreen(true);
1048 scheduleConfigure();
1049 } else {
1050 m_initialStates |= XdgToplevelInterface::State::FullScreen;
1051 }
1052}
1053
1054void XdgToplevelWindow::handleUnfullscreenRequested()
1055{
1056 m_fullScreenRequestedOutput.clear();
1057 if (m_isInitialized) {
1058 setFullScreen(false);
1059 scheduleConfigure();
1060 } else {
1061 m_initialStates &= ~XdgToplevelInterface::State::FullScreen;
1062 }
1063}
1064
1065void XdgToplevelWindow::handleMinimizeRequested()
1066{
1067 setMinimized(true);
1068}
1069
1070void XdgToplevelWindow::handleTransientForChanged()
1071{
1072 SurfaceInterface *transientForSurface = nullptr;
1073 if (XdgToplevelInterface *parentToplevel = m_shellSurface->parentXdgToplevel()) {
1074 transientForSurface = parentToplevel->surface();
1075 }
1076 if (!transientForSurface) {
1077 transientForSurface = waylandServer()->findForeignTransientForSurface(surface());
1078 }
1079 Window *transientForWindow = waylandServer()->findWindow(transientForSurface);
1080 if (transientForWindow != transientFor()) {
1081 if (transientFor()) {
1082 transientFor()->removeTransient(this);
1083 }
1084 if (transientForWindow) {
1085 transientForWindow->addTransient(this);
1086 }
1087 setTransientFor(transientForWindow);
1088 }
1089 m_isTransient = transientForWindow;
1090}
1091
1092void XdgToplevelWindow::handleForeignTransientForChanged(SurfaceInterface *child)
1093{
1094 if (surface() == child) {
1095 handleTransientForChanged();
1096 }
1097}
1098
1099void XdgToplevelWindow::handlePingTimeout(quint32 serial)
1100{
1101 auto pingIt = m_pings.find(serial);
1102 if (pingIt == m_pings.end()) {
1103 return;
1104 }
1105 if (pingIt.value() == PingReason::CloseWindow) {
1106 qCDebug(KWIN_CORE) << "Final ping timeout on a close attempt, asking to kill:" << caption();
1107
1108 if (!m_killPrompt) {
1109 m_killPrompt = std::make_unique<KillPrompt>(this);
1110 }
1111 if (!m_killPrompt->isRunning()) {
1112 m_killPrompt->start();
1113 }
1114 }
1115 m_pings.erase(pingIt);
1116}
1117
1118void XdgToplevelWindow::handlePingDelayed(quint32 serial)
1119{
1120 auto it = m_pings.find(serial);
1121 if (it != m_pings.end()) {
1122 qCDebug(KWIN_CORE) << "First ping timeout:" << caption();
1123 setUnresponsive(true);
1124 }
1125}
1126
1127void XdgToplevelWindow::handlePongReceived(quint32 serial)
1128{
1129 if (m_pings.remove(serial)) {
1130 setUnresponsive(false);
1131 if (m_killPrompt) {
1132 m_killPrompt->quit();
1133 }
1134 }
1135}
1136
1137void XdgToplevelWindow::handleMaximumSizeChanged()
1138{
1139 updateCapabilities();
1140 Q_EMIT maximizeableChanged(isMaximizable());
1141}
1142
1143void XdgToplevelWindow::handleMinimumSizeChanged()
1144{
1145 updateCapabilities();
1146 Q_EMIT maximizeableChanged(isMaximizable());
1147}
1148
1149void XdgToplevelWindow::sendPing(PingReason reason)
1150{
1151 XdgShellInterface *shell = m_shellSurface->shell();
1152 XdgSurfaceInterface *surface = m_shellSurface->xdgSurface();
1153
1154 const quint32 serial = shell->ping(surface);
1155 m_pings.insert(serial, reason);
1156}
1157
1158MaximizeMode XdgToplevelWindow::initialMaximizeMode() const
1159{
1160 MaximizeMode maximizeMode = MaximizeRestore;
1161 if (m_initialStates & XdgToplevelInterface::State::MaximizedHorizontal) {
1162 maximizeMode = MaximizeMode(maximizeMode | MaximizeHorizontal);
1163 }
1164 if (m_initialStates & XdgToplevelInterface::State::MaximizedVertical) {
1165 maximizeMode = MaximizeMode(maximizeMode | MaximizeVertical);
1166 }
1167 return maximizeMode;
1168}
1169
1170bool XdgToplevelWindow::initialFullScreenMode() const
1171{
1172 return m_initialStates & XdgToplevelInterface::State::FullScreen;
1173}
1174
1175void XdgToplevelWindow::initialize()
1176{
1177 bool needsPlacement = isPlaceable();
1178 setupWindowRules();
1179
1180 // Move or resize the window only if enforced by a window rule.
1181 const QPointF forcedPosition = rules()->checkPositionSafe(invalidPoint, true);
1182 if (forcedPosition != invalidPoint) {
1183 move(forcedPosition);
1184 }
1185 const QSizeF forcedSize = rules()->checkSize(QSize(), true);
1186 if (forcedSize.isValid()) {
1187 resize(forcedSize);
1188 }
1189
1190 maximize(rules()->checkMaximize(initialMaximizeMode(), true));
1191 setFullScreen(rules()->checkFullScreen(initialFullScreenMode(), true));
1192 setOnActivities(rules()->checkActivity(activities(), true));
1193 setDesktops(rules()->checkDesktops(desktops(), true));
1194 setDesktopFileName(rules()->checkDesktopFile(desktopFileName(), true));
1195 setMinimized(rules()->checkMinimize(isMinimized(), true));
1196 setSkipTaskbar(rules()->checkSkipTaskbar(skipTaskbar(), true));
1197 setSkipPager(rules()->checkSkipPager(skipPager(), true));
1198 setSkipSwitcher(rules()->checkSkipSwitcher(skipSwitcher(), true));
1199 setKeepAbove(rules()->checkKeepAbove(keepAbove(), true));
1200 setKeepBelow(rules()->checkKeepBelow(keepBelow(), true));
1201 setShortcut(rules()->checkShortcut(shortcut().toString(), true));
1202 setNoBorder(rules()->checkNoBorder(noBorder(), true));
1203
1204 // Don't place the client if its position is set by a rule.
1205 if (rules()->checkPosition(invalidPoint, true) != invalidPoint) {
1206 needsPlacement = false;
1207 }
1208
1209 // Don't place the client if the maximize state is set by a rule.
1210 if (requestedMaximizeMode() != MaximizeRestore) {
1211 needsPlacement = false;
1212 }
1213
1214 workspace()->rulebook()->discardUsed(this, false); // Remove Apply Now rules.
1215 updateWindowRules(Rules::All);
1216
1217 if (isRequestedFullScreen()) {
1218 needsPlacement = false;
1219 }
1220 if (needsPlacement) {
1221 const QRectF area = workspace()->clientArea(PlacementArea, this, workspace()->activeOutput());
1222 workspace()->placement()->place(this, area);
1223 }
1224
1225 configureDecoration();
1226 scheduleConfigure();
1227 updateColorScheme();
1228 updateCapabilities();
1229 updateClientOutputs();
1230 setupWindowManagementInterface();
1231
1232 m_isInitialized = true;
1233}
1234
1235void XdgToplevelWindow::updateMaximizeMode(MaximizeMode maximizeMode)
1236{
1237 if (m_maximizeMode == maximizeMode) {
1238 return;
1239 }
1240 m_maximizeMode = maximizeMode;
1241 updateWindowRules(Rules::MaximizeVert | Rules::MaximizeHoriz);
1242 Q_EMIT maximizedChanged();
1243}
1244
1245void XdgToplevelWindow::updateFullScreenMode(bool set)
1246{
1247 if (m_isFullScreen == set) {
1248 return;
1249 }
1250 StackingUpdatesBlocker blocker1(workspace());
1251 m_isFullScreen = set;
1252 updateLayer();
1253 updateWindowRules(Rules::Fullscreen);
1254 Q_EMIT fullScreenChanged();
1255}
1256
1257void XdgToplevelWindow::updateCapabilities()
1258{
1259 XdgToplevelInterface::Capabilities caps = XdgToplevelInterface::Capability::WindowMenu;
1260
1261 if (isMaximizable()) {
1262 caps.setFlag(XdgToplevelInterface::Capability::Maximize);
1263 }
1264 if (isFullScreenable()) {
1265 caps.setFlag(XdgToplevelInterface::Capability::FullScreen);
1266 }
1267 if (isMinimizable()) {
1268 caps.setFlag(XdgToplevelInterface::Capability::Minimize);
1269 }
1270
1271 if (m_capabilities != caps) {
1272 m_capabilities = caps;
1273 m_shellSurface->sendWmCapabilities(caps);
1274 }
1275}
1276
1277QString XdgToplevelWindow::preferredColorScheme() const
1278{
1279 if (m_paletteInterface) {
1280 return rules()->checkDecoColor(m_paletteInterface->palette());
1281 }
1282 return rules()->checkDecoColor(QString());
1283}
1284
1285void XdgToplevelWindow::installAppMenu(AppMenuInterface *appMenu)
1286{
1287 m_appMenuInterface = appMenu;
1288
1289 auto updateMenu = [this](const AppMenuInterface::InterfaceAddress &address) {
1290 updateApplicationMenuServiceName(address.serviceName);
1291 updateApplicationMenuObjectPath(address.objectPath);
1292 };
1293 connect(m_appMenuInterface, &AppMenuInterface::addressChanged, this, updateMenu);
1294 updateMenu(appMenu->address());
1295}
1296
1297XdgToplevelWindow::DecorationMode XdgToplevelWindow::preferredDecorationMode() const
1298{
1299 if (!Decoration::DecorationBridge::hasPlugin()) {
1300 return DecorationMode::Client;
1301 } else if (m_userNoBorder || isRequestedFullScreen()) {
1302 return DecorationMode::None;
1303 }
1304
1305 if (m_xdgDecoration) {
1306 switch (m_xdgDecoration->preferredMode()) {
1307 case XdgToplevelDecorationV1Interface::Mode::Undefined:
1308 return DecorationMode::Server;
1309 case XdgToplevelDecorationV1Interface::Mode::None:
1310 return DecorationMode::None;
1311 case XdgToplevelDecorationV1Interface::Mode::Client:
1312 return DecorationMode::Client;
1313 case XdgToplevelDecorationV1Interface::Mode::Server:
1314 return DecorationMode::Server;
1315 }
1316 }
1317
1318 if (m_serverDecoration) {
1319 switch (m_serverDecoration->preferredMode()) {
1320 case ServerSideDecorationManagerInterface::Mode::None:
1321 return DecorationMode::None;
1322 case ServerSideDecorationManagerInterface::Mode::Client:
1323 return DecorationMode::Client;
1324 case ServerSideDecorationManagerInterface::Mode::Server:
1325 return DecorationMode::Server;
1326 }
1327 }
1328
1329 return DecorationMode::Client;
1330}
1331
1332void XdgToplevelWindow::clearDecoration()
1333{
1334 m_nextDecoration = nullptr;
1335}
1336
1337void XdgToplevelWindow::configureDecoration()
1338{
1339 const DecorationMode decorationMode = preferredDecorationMode();
1340 switch (decorationMode) {
1341 case DecorationMode::None:
1342 case DecorationMode::Client:
1343 clearDecoration();
1344 break;
1345 case DecorationMode::Server:
1346 if (!m_nextDecoration) {
1347 m_nextDecoration.reset(Workspace::self()->decorationBridge()->createDecoration(this));
1348 }
1349 break;
1350 }
1351
1352 // All decoration updates are synchronized to toplevel configure events.
1353 if (m_xdgDecoration) {
1354 configureXdgDecoration(decorationMode);
1355 } else if (m_serverDecoration) {
1356 configureServerDecoration(decorationMode);
1357 }
1358}
1359
1360void XdgToplevelWindow::configureXdgDecoration(DecorationMode decorationMode)
1361{
1362 switch (decorationMode) {
1363 case DecorationMode::None: // Faked as server side mode under the hood.
1364 m_xdgDecoration->sendConfigure(XdgToplevelDecorationV1Interface::Mode::None);
1365 break;
1366 case DecorationMode::Client:
1367 m_xdgDecoration->sendConfigure(XdgToplevelDecorationV1Interface::Mode::Client);
1368 break;
1369 case DecorationMode::Server:
1370 m_xdgDecoration->sendConfigure(XdgToplevelDecorationV1Interface::Mode::Server);
1371 break;
1372 }
1373 scheduleConfigure();
1374}
1375
1376void XdgToplevelWindow::configureServerDecoration(DecorationMode decorationMode)
1377{
1378 switch (decorationMode) {
1379 case DecorationMode::None:
1380 m_serverDecoration->setMode(ServerSideDecorationManagerInterface::Mode::None);
1381 break;
1382 case DecorationMode::Client:
1383 m_serverDecoration->setMode(ServerSideDecorationManagerInterface::Mode::Client);
1384 break;
1385 case DecorationMode::Server:
1386 m_serverDecoration->setMode(ServerSideDecorationManagerInterface::Mode::Server);
1387 break;
1388 }
1389 scheduleConfigure();
1390}
1391
1392void XdgToplevelWindow::installXdgDecoration(XdgToplevelDecorationV1Interface *decoration)
1393{
1394 m_xdgDecoration = decoration;
1395
1396 connect(m_xdgDecoration, &XdgToplevelDecorationV1Interface::destroyed,
1397 this, &XdgToplevelWindow::clearDecoration);
1398 connect(m_xdgDecoration, &XdgToplevelDecorationV1Interface::preferredModeChanged, this, [this] {
1399 if (m_isInitialized) {
1400 configureDecoration();
1401 }
1402 });
1403}
1404
1405void XdgToplevelWindow::installServerDecoration(ServerSideDecorationInterface *decoration)
1406{
1407 m_serverDecoration = decoration;
1408 if (m_isInitialized) {
1409 configureDecoration();
1410 }
1411
1412 connect(m_serverDecoration, &ServerSideDecorationInterface::destroyed,
1413 this, &XdgToplevelWindow::clearDecoration);
1414 connect(m_serverDecoration, &ServerSideDecorationInterface::preferredModeChanged, this, [this]() {
1415 if (m_isInitialized) {
1416 configureDecoration();
1417 }
1418 });
1419}
1420
1421void XdgToplevelWindow::installPalette(ServerSideDecorationPaletteInterface *palette)
1422{
1423 m_paletteInterface = palette;
1424
1425 connect(m_paletteInterface, &ServerSideDecorationPaletteInterface::paletteChanged,
1426 this, &XdgToplevelWindow::updateColorScheme);
1427 connect(m_paletteInterface, &QObject::destroyed,
1428 this, &XdgToplevelWindow::updateColorScheme);
1429 updateColorScheme();
1430}
1431
1432void XdgToplevelWindow::setFullScreen(bool set)
1433{
1434 if (!isFullScreenable()) {
1435 return;
1436 }
1437
1438 set = rules()->checkFullScreen(set);
1439 if (m_isRequestedFullScreen == set) {
1440 return;
1441 }
1442
1443 m_isRequestedFullScreen = set;
1444 configureDecoration();
1445
1446 if (set) {
1447 const Output *output = m_fullScreenRequestedOutput ? m_fullScreenRequestedOutput.data() : moveResizeOutput();
1448 setFullscreenGeometryRestore(moveResizeGeometry());
1449 moveResize(workspace()->clientArea(FullScreenArea, this, output));
1450 } else {
1451 m_fullScreenRequestedOutput.clear();
1452 if (fullscreenGeometryRestore().isValid()) {
1453 moveResize(QRectF(fullscreenGeometryRestore().topLeft(),
1454 constrainFrameSize(fullscreenGeometryRestore().size())));
1455 } else {
1456 // this can happen when the window was first shown already fullscreen,
1457 // so let the client set the size by itself
1458 moveResize(QRectF(workspace()->clientArea(PlacementArea, this).topLeft(), QSize(0, 0)));
1459 }
1460 }
1461
1462 doSetFullScreen();
1463}
1464
1465static bool changeMaximizeRecursion = false;
1466void XdgToplevelWindow::maximize(MaximizeMode mode)
1467{
1468 if (changeMaximizeRecursion) {
1469 return;
1470 }
1471
1472 if (!isResizable() || isAppletPopup()) {
1473 return;
1474 }
1475
1476 const QRectF clientArea = isElectricBorderMaximizing() ? workspace()->clientArea(MaximizeArea, this, Cursors::self()->mouse()->pos()) : workspace()->clientArea(MaximizeArea, this, moveResizeOutput());
1477
1478 const MaximizeMode oldMode = m_requestedMaximizeMode;
1479 const QRectF oldGeometry = moveResizeGeometry();
1480
1481 mode = rules()->checkMaximize(mode);
1482 if (m_requestedMaximizeMode == mode) {
1483 return;
1484 }
1485
1486 Q_EMIT maximizedAboutToChange(mode);
1487 m_requestedMaximizeMode = mode;
1488
1489 // call into decoration update borders
1490 if (m_nextDecoration && !(options->borderlessMaximizedWindows() && m_requestedMaximizeMode == MaximizeFull)) {
1491 changeMaximizeRecursion = true;
1492 const auto c = m_nextDecoration->client();
1493 if ((m_requestedMaximizeMode & MaximizeVertical) != (oldMode & MaximizeVertical)) {
1494 Q_EMIT c->maximizedVerticallyChanged(m_requestedMaximizeMode & MaximizeVertical);
1495 }
1496 if ((m_requestedMaximizeMode & MaximizeHorizontal) != (oldMode & MaximizeHorizontal)) {
1497 Q_EMIT c->maximizedHorizontallyChanged(m_requestedMaximizeMode & MaximizeHorizontal);
1498 }
1499 if ((m_requestedMaximizeMode == MaximizeFull) != (oldMode == MaximizeFull)) {
1500 Q_EMIT c->maximizedChanged(m_requestedMaximizeMode == MaximizeFull);
1501 }
1502 changeMaximizeRecursion = false;
1503 }
1504
1506 setNoBorder(m_requestedMaximizeMode == MaximizeFull);
1507 }
1508
1509 if (quickTileMode() == QuickTileMode(QuickTileFlag::None)) {
1510 QRectF savedGeometry = geometryRestore();
1511 if (!(oldMode & MaximizeVertical)) {
1512 savedGeometry.setTop(oldGeometry.top());
1513 savedGeometry.setBottom(oldGeometry.bottom());
1514 }
1515 if (!(oldMode & MaximizeHorizontal)) {
1516 savedGeometry.setLeft(oldGeometry.left());
1517 savedGeometry.setRight(oldGeometry.right());
1518 }
1519 setGeometryRestore(savedGeometry);
1520 }
1521
1522 const MaximizeMode delta = m_requestedMaximizeMode ^ oldMode;
1523 QRectF geometry = oldGeometry;
1524
1525 if (delta & MaximizeHorizontal) {
1526 if (m_requestedMaximizeMode & MaximizeHorizontal) {
1527 // Stretch the window vertically to fit the size of the maximize area.
1528 geometry.setX(clientArea.x());
1529 geometry.setWidth(clientArea.width());
1530 } else if (geometryRestore().isValid()) {
1531 // The window is no longer maximized horizontally and the saved geometry is valid.
1532 geometry.setX(geometryRestore().x());
1533 geometry.setWidth(geometryRestore().width());
1534 } else {
1535 // The window is no longer maximized horizontally and the saved geometry is
1536 // invalid. This would happen if the window had been mapped in the maximized state.
1537 // We ask the client to resize the window horizontally to its preferred size.
1538 geometry.setX(clientArea.x());
1539 geometry.setWidth(0);
1540 }
1541 }
1542
1543 if (delta & MaximizeVertical) {
1544 if (m_requestedMaximizeMode & MaximizeVertical) {
1545 // Stretch the window horizontally to fit the size of the maximize area.
1546 geometry.setY(clientArea.y());
1547 geometry.setHeight(clientArea.height());
1548 } else if (geometryRestore().isValid()) {
1549 // The window is no longer maximized vertically and the saved geometry is valid.
1550 geometry.setY(geometryRestore().y());
1551 geometry.setHeight(geometryRestore().height());
1552 } else {
1553 // The window is no longer maximized vertically and the saved geometry is
1554 // invalid. This would happen if the window had been mapped in the maximized state.
1555 // We ask the client to resize the window vertically to its preferred size.
1556 geometry.setY(clientArea.y());
1557 geometry.setHeight(0);
1558 }
1559 }
1560
1561 const auto oldQuickTileMode = quickTileMode();
1562 if (m_requestedMaximizeMode == MaximizeFull) {
1564 updateQuickTileMode(QuickTileFlag::Maximize);
1565 } else {
1566 updateQuickTileMode(QuickTileFlag::None);
1567 }
1568 setTile(nullptr);
1569 } else {
1570 updateQuickTileMode(QuickTileFlag::None);
1571 }
1572
1573 moveResize(geometry);
1574
1575 if (oldQuickTileMode != quickTileMode()) {
1576 doSetQuickTileMode();
1577 Q_EMIT quickTileModeChanged();
1578 }
1579
1580 doSetMaximized();
1581}
1582
1583XdgPopupWindow::XdgPopupWindow(XdgPopupInterface *shellSurface)
1584 : XdgSurfaceWindow(shellSurface->xdgSurface())
1585 , m_shellSurface(shellSurface)
1586{
1587 m_windowType = NET::Unknown;
1588
1589 connect(shellSurface, &XdgPopupInterface::grabRequested,
1590 this, &XdgPopupWindow::handleGrabRequested);
1591 connect(shellSurface, &XdgPopupInterface::initializeRequested,
1592 this, &XdgPopupWindow::initialize);
1593 connect(shellSurface, &XdgPopupInterface::repositionRequested,
1594 this, &XdgPopupWindow::handleRepositionRequested);
1595 connect(shellSurface, &XdgPopupInterface::aboutToBeDestroyed,
1597}
1598
1600{
1602 this, &XdgPopupWindow::relayout);
1603 m_shellSurface->disconnect(this);
1604
1606}
1607
1608void XdgPopupWindow::handleRepositionRequested(quint32 token)
1609{
1610 updateRelativePlacement();
1611 m_shellSurface->sendRepositioned(token);
1612 relayout();
1613}
1614
1615void XdgPopupWindow::updateRelativePlacement()
1616{
1617 const QPointF parentPosition = transientFor()->framePosToClientPos(transientFor()->pos());
1618 const QRectF bounds = workspace()->clientArea(transientFor()->isFullScreen() ? FullScreenArea : PlacementArea, transientFor()).translated(-parentPosition);
1619 const XdgPositioner positioner = m_shellSurface->positioner();
1620
1621 if (m_plasmaShellSurface && m_plasmaShellSurface->isPositionSet()) {
1622 m_relativePlacement = QRectF(m_plasmaShellSurface->position(), positioner.size()).translated(-parentPosition);
1623 } else {
1624 m_relativePlacement = positioner.placement(bounds);
1625 }
1626}
1627
1628void XdgPopupWindow::relayout()
1629{
1630 if (m_shellSurface->positioner().isReactive()) {
1631 updateRelativePlacement();
1632 }
1633 workspace()->placement()->place(this, QRectF());
1635}
1636
1640
1642{
1643 return m_haveExplicitGrab;
1644}
1645
1647{
1648 m_shellSurface->sendPopupDone();
1649}
1650
1652{
1653 return true;
1654}
1655
1657{
1658 return true;
1659}
1660
1662{
1663 return false;
1664}
1665
1667{
1668 return false;
1669}
1670
1672{
1673 return false;
1674}
1675
1677{
1678 return true;
1679}
1680
1682{
1683 const QPointF parentPosition = transientFor()->framePosToClientPos(transientFor()->pos());
1684 return m_relativePlacement.translated(parentPosition);
1685}
1686
1688{
1689 return false;
1690}
1691
1695
1697{
1698 return false;
1699}
1700
1702{
1703 return false;
1704}
1705
1707{
1708 return false;
1709}
1710
1712{
1713 const QPointF parentPosition = transientFor()->framePosToClientPos(transientFor()->pos());
1714 const QPointF popupPosition = moveResizeGeometry().topLeft() - parentPosition;
1715
1716 const quint32 serial = m_shellSurface->sendConfigure(QRect(popupPosition.toPoint(), moveResizeGeometry().size().toSize()));
1717
1718 XdgSurfaceConfigure *configureEvent = new XdgSurfaceConfigure();
1719 configureEvent->bounds = moveResizeGeometry();
1720 configureEvent->serial = serial;
1721
1722 return configureEvent;
1723}
1724
1725void XdgPopupWindow::handleGrabRequested(SeatInterface *seat, quint32 serial)
1726{
1727 m_haveExplicitGrab = true;
1728}
1729
1730void XdgPopupWindow::initialize()
1731{
1732 Window *parent = waylandServer()->findWindow(m_shellSurface->parentSurface());
1733 parent->addTransient(this);
1734 setTransientFor(parent);
1735 setDesktops(parent->desktops());
1736#if KWIN_BUILD_ACTIVITIES
1737 setOnActivities(parent->activities());
1738#endif
1739
1740 updateRelativePlacement();
1741 connect(parent, &Window::frameGeometryChanged, this, &XdgPopupWindow::relayout);
1742
1743 workspace()->placement()->place(this, QRectF());
1745}
1746
1747} // namespace KWin
1748
1749#include "moc_xdgshellwindow.cpp"
TabletInputRedirection * tablet() const
Definition input.h:224
TouchInputRedirection * touch() const
Definition input.h:228
PointerInputRedirection * pointer() const
Definition input.h:220
bool borderlessMaximizedWindows
Definition options.h:172
bool electricBorderMaximize
Definition options.h:163
void place(Window *c, const QRectF &area)
Definition placement.cpp:40
Resource for the org_kde_plasma_shell_surface interface.
Definition plasmashell.h:60
@ Desktop
The surface represents a desktop, normally stacked below all other surfaces.
@ CriticalNotification
The surface represents a critical notification, like battery is running out.
@ Notification
The surface represents a notification.
@ Panel
The surface represents a panel (dock), normally stacked above normal surfaces.
@ OnScreenDisplay
The surface represents an on screen display, like a volume changed notification.
@ ToolTip
The surface represents a tooltip.
@ AppletPopup
The surface represents an applet popup window.
void discardUsed(Window *c, bool withdraw)
Definition rules.cpp:936
Represents a Seat on the Wayland Display.
Definition seat.h:134
Representing how a SurfaceInterface should be decorated.
QRectF boundingRect() const
Definition surface.cpp:855
QPointF position() const override
QPointF position() const override
Definition touch_input.h:64
SurfaceInterface * findForeignTransientForSurface(SurfaceInterface *surface)
Window * findWindow(const SurfaceInterface *surface) const
void removeWindow(Window *c)
void foreignTransientChanged(KWin::SurfaceInterface *child)
void updateGeometry(const QRectF &rect)
virtual QSizeF clientSizeToFrameSize(const QSizeF &size) const
Definition window.cpp:3265
QPointF pos
Definition window.h:79
QRectF frameGeometry
Definition window.h:431
void frameGeometryAboutToChange()
virtual void updateWindowRules(Rules::Types selection)
Definition window.cpp:4087
virtual QPointF framePosToClientPos(const QPointF &point) const
Definition window.cpp:3248
void keepInArea(QRectF area, bool partial=false)
Definition window.cpp:1129
void setDecoration(std::shared_ptr< KDecoration2::Decoration > decoration)
Definition window.cpp:2625
QSizeF size
Definition window.h:84
bool isShade() const
Definition window.h:1034
SurfaceInterface * surface() const
Definition window.cpp:342
void setSkipTaskbar(bool set)
Definition window.cpp:464
virtual void destroyWindow()=0
void setTransientFor(Window *transientFor)
Definition window.cpp:2217
virtual void applyWindowRules()
Definition window.cpp:4104
bool isInteractiveMoveResize() const
Definition window.h:1570
void windowShown(KWin::Window *window)
void unref()
Definition window.cpp:118
std::shared_ptr< KDecoration2::Decoration > decoration
Definition window.h:1820
void setOnAllDesktops(bool set)
Definition window.cpp:813
virtual bool isFullScreen() const
Definition window.cpp:3935
QSizeF clientSize() const
Definition window.h:1872
bool move
Definition window.h:437
void interactiveMoveResizeFinished()
void setDesktops(QList< VirtualDesktop * > desktops)
Definition window.cpp:726
bool isAppletPopup() const
Definition window.h:1987
int borderLeft() const
Definition window.cpp:2361
bool isDesktop() const
Definition window.h:1922
bool isToolbar() const
Definition window.h:1937
void destroyWindowManagementInterface()
Definition window.cpp:1975
void cleanTabBox()
Definition window.cpp:4054
int borderTop() const
Definition window.cpp:2371
virtual void leaveInteractiveMoveResize()
Definition window.cpp:2423
Output * moveResizeOutput() const
Definition window.cpp:3297
QRectF moveResizeGeometry() const
Definition window.cpp:3286
QRectF rect
Definition window.h:113
const WindowRules * rules() const
Definition window.h:1048
bool isSplash() const
Definition window.h:1942
void setMoveResizeGeometry(const QRectF &geo)
Definition window.cpp:3291
virtual void removeTransient(Window *transient)
Definition window.cpp:2329
KWin::Window * transientFor
Definition window.h:420
bool isDock() const
Definition window.h:1927
void frameGeometryChanged(const QRectF &oldGeometry)
void updateShadow()
Definition window.cpp:264
bool isSpecialWindow() const
Definition window.cpp:702
void setOnAllActivities(bool all)
Definition window.cpp:3191
bool areGeometryUpdatesBlocked() const
Definition window.h:2093
void setPendingMoveResizeMode(MoveResizeMode mode)
Definition window.h:2113
void setOnActivities(const QStringList &newActivitiesList)
Definition window.cpp:3144
virtual QSizeF frameSizeToClientSize(const QSizeF &size) const
Definition window.cpp:3258
void setSkipSwitcher(bool set)
Definition window.cpp:436
bool isDeleted() const
Definition window.cpp:540
void markAsDeleted()
Definition window.cpp:545
QSizeF checkMaxSize(QSizeF s) const
bool checkNoBorder(bool noborder, bool init=false) const
bool checkCloseable(bool closeable) const
QSizeF checkMinSize(QSizeF s) const
QRectF clientArea(clientAreaOption, const Output *output, const VirtualDesktop *desktop) const
void windowHidden(Window *)
void updateMinimizedOfTransients(Window *)
Placement * placement() const
static Workspace * self()
Definition workspace.h:91
RuleBook * rulebook() const
void repositionRequested(quint32 token)
void grabRequested(SeatInterface *seat, quint32 serial)
void sendRepositioned(quint32 token)
Definition xdgshell.cpp:829
SurfaceInterface * parentSurface() const
Definition xdgshell.cpp:786
quint32 sendConfigure(const QRect &rect)
Definition xdgshell.cpp:811
XdgPositioner positioner() const
Definition xdgshell.cpp:806
QRectF transientPlacement() const override
bool isMovable() const override
bool takeFocus() override
bool hasPopupGrab() const override
bool isPopupWindow() const override
bool isCloseable() const override
bool isResizable() const override
XdgSurfaceConfigure * sendRoleConfigure() const override
bool isTransient() const override
bool isMovableAcrossScreens() const override
void closeWindow() override
bool acceptsFocus() const override
bool hasTransientPlacementHint() const override
bool wantsInput() const override
void popupDone() override
void handleRoleDestroyed() override
bool isReactive() const
void pingTimeout(quint32 serial)
void pingDelayed(quint32 serial)
void pongReceived(quint32 serial)
QRect windowGeometry() const
Definition xdgshell.cpp:319
void windowGeometryChanged(const QRect &rect)
SurfaceInterface * surface() const
Definition xdgshell.cpp:309
void configureAcknowledged(quint32 serial)
NET::WindowType m_windowType
virtual void handleRoleDestroyed()
NET::WindowType windowType() const override
XdgSurfaceWindow(XdgSurfaceInterface *shellSurface)
void destroyWindow() override
void installPlasmaShellSurface(PlasmaShellSurfaceInterface *shellSurface)
XdgSurfaceConfigure * lastAcknowledgedConfigure() const
QRectF frameRectToBufferRect(const QRectF &rect) const override
virtual XdgSurfaceConfigure * sendRoleConfigure() const =0
virtual void handleRoleCommit()
virtual void handleRolePrecommit()
void moveResizeInternal(const QRectF &rect, MoveResizeMode mode) override
QPointer< PlasmaShellSurfaceInterface > m_plasmaShellSurface
std::shared_ptr< KDecoration2::Decoration > decoration
XdgToplevelInterface::States states
void windowClassChanged(const QString &windowClass)
void windowMenuRequested(KWin::SeatInterface *seat, const QPoint &pos, quint32 serial)
XdgShellInterface * shell() const
Definition xdgshell.cpp:542
void minimumSizeChanged(const QSize &size)
quint32 sendConfigure(const QSize &size, const States &states)
Definition xdgshell.cpp:587
void windowTitleChanged(const QString &windowTitle)
void fullscreenRequested(KWin::OutputInterface *output)
void maximumSizeChanged(const QSize &size)
void moveRequested(KWin::SeatInterface *seat, quint32 serial)
void sendConfigureBounds(const QSize &size)
Definition xdgshell.cpp:645
void resizeRequested(KWin::SeatInterface *seat, KWin::XdgToplevelInterface::ResizeAnchor anchor, quint32 serial)
XdgToplevelInterface * shellSurface() const
bool isCloseable() const override
bool noBorder() const override
MaximizeMode requestedMaximizeMode() const override
bool isRequestedFullScreen() const override
bool isMovable() const override
XdgToplevelWindow(XdgToplevelInterface *shellSurface)
QSizeF maxSize() const override
bool isFullScreenable() const override
bool userCanSetNoBorder() const override
void handleRoleDestroyed() override
bool isTransient() const override
QSizeF minSize() const override
bool isPlaceable() const override
void applyWindowRules() override
bool isFullScreen() const override
bool supportsWindowRules() const override
bool isMaximizable() const override
MaximizeMode maximizeMode() const override
void setNoBorder(bool set) override
bool isResizable() const override
void invalidateDecoration() override
bool isMovableAcrossScreens() const override
void handleRolePrecommit() override
bool isMinimizable() const override
XdgSurfaceConfigure * sendRoleConfigure() const override
KWayland::Client::Seat * seat
Gravity
Definition globals.h:150
Session::Type type
Definition session.cpp:17
@ FullScreenArea
Definition globals.h:53
@ MaximizeArea
Definition globals.h:51
@ PlacementArea
Definition globals.h:49
const QPoint invalidPoint(INT_MIN, INT_MIN)
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
WaylandServer * waylandServer()
Workspace * workspace()
Definition workspace.h:830
QRectF gravitateGeometry(const QRectF &rect, const QRectF &bounds, Gravity gravity)
Definition common.cpp:246
Options * options
Definition main.cpp:73
InputRedirection * input()
Definition input.h:549