1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtWaylandCompositor module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 or (at your option) any later version
20** approved by the KDE Free Qt Foundation. The licenses are as published by
21** the Free Software Foundation and appearing in the file LICENSE.GPL3
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29
30#include "qwaylandxdgshellv5integration_p.h"
31
32#include <QtWaylandCompositor/QWaylandQuickShellSurfaceItem>
33#include <QtWaylandCompositor/QWaylandCompositor>
34#include <QtWaylandCompositor/QWaylandSeat>
35#include <QtWaylandCompositor/private/qwaylandxdgshellv5_p.h>
36#include <QMouseEvent>
37#include <QGuiApplication>
38
39QT_BEGIN_NAMESPACE
40
41#if QT_DEPRECATED_SINCE(5, 15)
42
43namespace QtWayland {
44
45static void handlePopupCreated(QWaylandQuickShellSurfaceItem *parentItem, QWaylandXdgPopupV5 *popup)
46{
47 if (parentItem->surface() == popup->parentSurface())
48 QWaylandQuickShellSurfaceItemPrivate::get(item: parentItem)->maybeCreateAutoPopup(shellSurface: popup);
49}
50
51XdgShellV5Integration::XdgShellV5Integration(QWaylandQuickShellSurfaceItem *item)
52 : QWaylandQuickShellIntegration(item)
53 , m_item(item)
54 , m_xdgSurface(qobject_cast<QWaylandXdgSurfaceV5 *>(object: item->shellSurface()))
55 , grabberState(GrabberState::Default)
56{
57 m_item->setSurface(m_xdgSurface->surface());
58 connect(sender: m_xdgSurface, signal: &QWaylandXdgSurfaceV5::startMove, receiver: this, slot: &XdgShellV5Integration::handleStartMove);
59 connect(sender: m_xdgSurface, signal: &QWaylandXdgSurfaceV5::startResize, receiver: this, slot: &XdgShellV5Integration::handleStartResize);
60 connect(sender: m_xdgSurface, signal: &QWaylandXdgSurfaceV5::setTopLevel, receiver: this, slot: &XdgShellV5Integration::handleSetTopLevel);
61 connect(sender: m_xdgSurface, signal: &QWaylandXdgSurfaceV5::setTransient, receiver: this, slot: &XdgShellV5Integration::handleSetTransient);
62 connect(sender: m_xdgSurface, signal: &QWaylandXdgSurfaceV5::setMaximized, receiver: this, slot: &XdgShellV5Integration::handleSetMaximized);
63 connect(sender: m_xdgSurface, signal: &QWaylandXdgSurfaceV5::unsetMaximized, receiver: this, slot: &XdgShellV5Integration::handleUnsetMaximized);
64 connect(sender: m_xdgSurface, signal: &QWaylandXdgSurfaceV5::maximizedChanged, receiver: this, slot: &XdgShellV5Integration::handleMaximizedChanged);
65 connect(sender: m_xdgSurface, signal: &QWaylandXdgSurfaceV5::activatedChanged, receiver: this, slot: &XdgShellV5Integration::handleActivatedChanged);
66 connect(sender: m_xdgSurface->surface(), signal: &QWaylandSurface::destinationSizeChanged, receiver: this, slot: &XdgShellV5Integration::handleSurfaceSizeChanged);
67 connect(sender: m_xdgSurface->shell(), signal: &QWaylandXdgShellV5::xdgPopupCreated, context: this, slot: [item](QWaylandXdgPopupV5 *popup){
68 handlePopupCreated(parentItem: item, popup);
69 });
70}
71
72XdgShellV5Integration::~XdgShellV5Integration()
73{
74 m_item->setSurface(nullptr);
75}
76
77bool XdgShellV5Integration::eventFilter(QObject *object, QEvent *event)
78{
79 if (event->type() == QEvent::MouseMove) {
80 QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
81 return filterMouseMoveEvent(event: mouseEvent);
82 } else if (event->type() == QEvent::MouseButtonRelease) {
83 QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
84 return filterMouseReleaseEvent(event: mouseEvent);
85 }
86 return QWaylandQuickShellIntegration::eventFilter(watched: object, event);
87}
88
89bool XdgShellV5Integration::filterMouseMoveEvent(QMouseEvent *event)
90{
91 if (grabberState == GrabberState::Resize) {
92 Q_ASSERT(resizeState.seat == m_item->compositor()->seatFor(event));
93 if (!resizeState.initialized) {
94 resizeState.initialMousePos = event->windowPos();
95 resizeState.initialized = true;
96 return true;
97 }
98 QPointF delta = m_item->mapToSurface(point: event->windowPos() - resizeState.initialMousePos);
99 QSize newSize = m_xdgSurface->sizeForResize(size: resizeState.initialWindowSize, delta, edge: resizeState.resizeEdges);
100 m_xdgSurface->sendResizing(maxSize: newSize);
101 } else if (grabberState == GrabberState::Move) {
102 Q_ASSERT(moveState.seat == m_item->compositor()->seatFor(event));
103 QQuickItem *moveItem = m_item->moveItem();
104 if (!moveState.initialized) {
105 moveState.initialOffset = moveItem->mapFromItem(item: nullptr, point: event->windowPos());
106 moveState.initialized = true;
107 return true;
108 }
109 if (!moveItem->parentItem())
110 return true;
111 QPointF parentPos = moveItem->parentItem()->mapFromItem(item: nullptr, point: event->windowPos());
112 moveItem->setPosition(parentPos - moveState.initialOffset);
113 }
114 return false;
115}
116
117bool XdgShellV5Integration::filterMouseReleaseEvent(QMouseEvent *event)
118{
119 Q_UNUSED(event);
120
121 if (grabberState == GrabberState::Resize) {
122 m_xdgSurface->sendUnmaximized();
123 grabberState = GrabberState::Default;
124 return true;
125 } else if (grabberState == GrabberState::Move) {
126 grabberState = GrabberState::Default;
127 return true;
128 }
129 return false;
130}
131
132void XdgShellV5Integration::handleStartMove(QWaylandSeat *seat)
133{
134 grabberState = GrabberState::Move;
135 moveState.seat = seat;
136 moveState.initialized = false;
137}
138
139void XdgShellV5Integration::handleStartResize(QWaylandSeat *seat, QWaylandXdgSurfaceV5::ResizeEdge edges)
140{
141 grabberState = GrabberState::Resize;
142 resizeState.seat = seat;
143 resizeState.resizeEdges = edges;
144 resizeState.initialWindowSize = m_xdgSurface->windowGeometry().size();
145 resizeState.initialPosition = m_item->moveItem()->position();
146 resizeState.initialSurfaceSize = m_item->surface()->destinationSize();
147 resizeState.initialized = false;
148}
149
150void XdgShellV5Integration::handleSetTopLevel()
151{
152 if (m_xdgSurface->shell()->focusPolicy() == QWaylandShell::AutomaticFocus)
153 m_item->takeFocus();
154}
155
156void XdgShellV5Integration::handleSetTransient()
157{
158 if (m_xdgSurface->shell()->focusPolicy() == QWaylandShell::AutomaticFocus)
159 m_item->takeFocus();
160}
161
162void XdgShellV5Integration::handleSetMaximized()
163{
164 if (!m_item->view()->isPrimary())
165 return;
166
167 maximizeState.initialWindowSize = m_xdgSurface->windowGeometry().size();
168 maximizeState.initialPosition = m_item->moveItem()->position();
169
170 QWaylandOutput *output = m_item->view()->output();
171 m_xdgSurface->sendMaximized(size: output->availableGeometry().size() / output->scaleFactor());
172}
173
174void XdgShellV5Integration::handleUnsetMaximized()
175{
176 if (!m_item->view()->isPrimary())
177 return;
178
179 m_xdgSurface->sendUnmaximized(size: maximizeState.initialWindowSize);
180}
181
182void XdgShellV5Integration::handleMaximizedChanged()
183{
184 if (m_xdgSurface->maximized()) {
185 QWaylandOutput *output = m_item->view()->output();
186 m_item->moveItem()->setPosition(output->position() + output->availableGeometry().topLeft());
187 } else {
188 m_item->moveItem()->setPosition(maximizeState.initialPosition);
189 }
190}
191
192void XdgShellV5Integration::handleActivatedChanged()
193{
194 if (m_xdgSurface->activated())
195 m_item->raise();
196}
197
198void XdgShellV5Integration::handleSurfaceSizeChanged()
199{
200 if (grabberState == GrabberState::Resize) {
201 qreal dx = 0;
202 qreal dy = 0;
203 if (resizeState.resizeEdges & QWaylandXdgSurfaceV5::ResizeEdge::TopEdge)
204 dy = resizeState.initialSurfaceSize.height() - m_item->surface()->destinationSize().height();
205 if (resizeState.resizeEdges & QWaylandXdgSurfaceV5::ResizeEdge::LeftEdge)
206 dx = resizeState.initialSurfaceSize.width() - m_item->surface()->destinationSize().width();
207 QPointF offset = m_item->mapFromSurface(point: {dx, dy});
208 m_item->moveItem()->setPosition(resizeState.initialPosition + offset);
209 }
210}
211
212XdgPopupV5Integration::XdgPopupV5Integration(QWaylandQuickShellSurfaceItem *item)
213 : QWaylandQuickShellIntegration (item)
214 , m_item(item)
215 , m_xdgPopup(qobject_cast<QWaylandXdgPopupV5 *>(object: item->shellSurface()))
216 , m_xdgShell(QWaylandXdgPopupV5Private::get(xdgPopup: m_xdgPopup)->m_xdgShell)
217{
218 item->setSurface(m_xdgPopup->surface());
219 if (item->view()->output()) {
220 QPoint position = item->mapFromSurface(point: m_xdgPopup->position()).toPoint();
221 item->moveItem()->setPosition(position);
222 } else {
223 qWarning() << "XdgPopupV5Integration popup item without output" << item;
224 }
225
226 QWaylandClient *client = m_xdgPopup->surface()->client();
227 auto shell = m_xdgShell;
228 QWaylandQuickShellEventFilter::startFilter(client, closePopupCallback: [shell]() { shell->closeAllPopups(); });
229
230 connect(sender: m_xdgPopup, signal: &QWaylandXdgPopupV5::destroyed, receiver: this, slot: &XdgPopupV5Integration::handlePopupDestroyed);
231 connect(sender: m_xdgPopup->shell(), signal: &QWaylandXdgShellV5::xdgPopupCreated, context: this, slot: [item](QWaylandXdgPopupV5 *popup) {
232 handlePopupCreated(parentItem: item, popup);
233 });
234}
235
236XdgPopupV5Integration::~XdgPopupV5Integration()
237{
238 m_item->setSurface(nullptr);
239}
240
241void XdgPopupV5Integration::handlePopupDestroyed()
242{
243 QWaylandXdgShellV5Private *shellPrivate = QWaylandXdgShellV5Private::get(xdgShell: m_xdgShell);
244 auto popups = shellPrivate->m_xdgPopups;
245 if (popups.isEmpty())
246 QWaylandQuickShellEventFilter::cancelFilter();
247}
248
249}
250
251#endif // QT_DEPRECATED_SINCE(5, 15)
252
253QT_END_NAMESPACE
254

source code of qtwayland/src/compositor/extensions/qwaylandxdgshellv5integration.cpp