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 "qwaylandquickshellsurfaceitem.h"
31#include "qwaylandquickshellsurfaceitem_p.h"
32
33#include <QtWaylandCompositor/QWaylandShellSurface>
34#include <QGuiApplication>
35
36QT_BEGIN_NAMESPACE
37
38QWaylandQuickShellSurfaceItem *QWaylandQuickShellSurfaceItemPrivate::maybeCreateAutoPopup(QWaylandShellSurface* shellSurface)
39{
40 if (!m_autoCreatePopupItems)
41 return nullptr;
42
43 Q_Q(QWaylandQuickShellSurfaceItem);
44 auto *popupItem = new QWaylandQuickShellSurfaceItem(q);
45 popupItem->setShellSurface(shellSurface);
46 popupItem->setAutoCreatePopupItems(true);
47 QObject::connect(sender: popupItem, signal: &QWaylandQuickShellSurfaceItem::surfaceDestroyed, slot: [popupItem](){
48 popupItem->deleteLater();
49 });
50 return popupItem;
51}
52
53/*!
54 * \qmltype ShellSurfaceItem
55 * \instantiates QWaylandQuickShellSurfaceItem
56 * \inherits WaylandQuickItem
57 * \inqmlmodule QtWayland.Compositor
58 * \since 5.8
59 * \brief A Qt Quick item type for displaying and interacting with a ShellSurface.
60 *
61 * This type is used to render \c wl_shell, \c xdg_shell or \c ivi_application surfaces as part of
62 * a Qt Quick scene. It handles moving and resizing triggered by clicking on the window decorations.
63 *
64 * \sa WaylandQuickItem, WlShellSurface, XdgSurfaceV5, IviSurface
65 */
66
67/*!
68 * \class QWaylandQuickShellSurfaceItem
69 * \inmodule QtWaylandCompositor
70 * \since 5.8
71 * \brief The QWaylandQuickShellSurfaceItem class provides a Qt Quick item that represents a QWaylandShellSurface.
72 *
73 * This class is used to render \c wl_shell, \c xdg_shell or \c ivi_application surfaces as part of
74 * a Qt Quick scene. It handles moving and resizing triggered by clicking on the window decorations.
75 *
76 * \sa QWaylandQuickItem, QWaylandWlShellSurface, QWaylandXdgSurfaceV5, QWaylandIviSurface
77 */
78
79/*!
80 * Constructs a QWaylandQuickWlShellSurfaceItem with the given \a parent.
81 */
82QWaylandQuickShellSurfaceItem::QWaylandQuickShellSurfaceItem(QQuickItem *parent)
83 : QWaylandQuickItem(*new QWaylandQuickShellSurfaceItemPrivate(), parent)
84{
85}
86
87QWaylandQuickShellSurfaceItem::~QWaylandQuickShellSurfaceItem()
88{
89 Q_D(QWaylandQuickShellSurfaceItem);
90
91 if (d->m_shellIntegration) {
92 removeEventFilter(obj: d->m_shellIntegration);
93 delete d->m_shellIntegration;
94 }
95}
96
97/*!
98 * \internal
99 */
100QWaylandQuickShellSurfaceItem::QWaylandQuickShellSurfaceItem(QWaylandQuickShellSurfaceItemPrivate &dd, QQuickItem *parent)
101 : QWaylandQuickItem(dd, parent)
102{
103}
104
105/*!
106 * \qmlproperty ShellSurface QtWaylandCompositor::ShellSurfaceItem::shellSurface
107 *
108 * This property holds the ShellSurface rendered by this ShellSurfaceItem.
109 * It may either be an XdgSurfaceV5, WlShellSurface or IviSurface depending on which shell protocol
110 * is in use.
111 */
112
113/*!
114 * \property QWaylandQuickShellSurfaceItem::shellSurface
115 *
116 * This property holds the QWaylandShellSurface rendered by this QWaylandQuickShellSurfaceItem.
117 * It may either be a QWaylandXdgSurfaceV5, QWaylandWlShellSurface or QWaylandIviSurface depending
118 * on which shell protocol is in use.
119 */
120QWaylandShellSurface *QWaylandQuickShellSurfaceItem::shellSurface() const
121{
122 Q_D(const QWaylandQuickShellSurfaceItem);
123 return d->m_shellSurface;
124}
125
126void QWaylandQuickShellSurfaceItem::setShellSurface(QWaylandShellSurface *shellSurface)
127{
128 Q_D(QWaylandQuickShellSurfaceItem);
129 if (d->m_shellSurface == shellSurface)
130 return;
131
132 d->m_shellSurface = shellSurface;
133
134 if (d->m_shellIntegration) {
135 removeEventFilter(obj: d->m_shellIntegration);
136 delete d->m_shellIntegration;
137 d->m_shellIntegration = nullptr;
138 }
139
140 if (shellSurface) {
141 d->m_shellIntegration = shellSurface->createIntegration(item: this);
142 installEventFilter(filterObj: d->m_shellIntegration);
143 }
144
145 emit shellSurfaceChanged();
146}
147
148/*!
149 * \qmlproperty Item QtWaylandCompositor::ShellSurfaceItem::moveItem
150 *
151 * This property holds the move item for this ShellSurfaceItem. This is the item that will be moved
152 * when the clients request the ShellSurface to be moved, maximized, resized etc. This property is
153 * useful when implementing server-side decorations.
154 */
155
156/*!
157 * \property QWaylandQuickShellSurfaceItem::moveItem
158 *
159 * This property holds the move item for this QWaylandQuickShellSurfaceItem. This is the item that
160 * will be moved when the clients request the QWaylandShellSurface to be moved, maximized, resized
161 * etc. This property is useful when implementing server-side decorations.
162 */
163QQuickItem *QWaylandQuickShellSurfaceItem::moveItem() const
164{
165 Q_D(const QWaylandQuickShellSurfaceItem);
166 return d->m_moveItem ? d->m_moveItem : const_cast<QWaylandQuickShellSurfaceItem *>(this);
167}
168
169void QWaylandQuickShellSurfaceItem::setMoveItem(QQuickItem *moveItem)
170{
171 Q_D(QWaylandQuickShellSurfaceItem);
172 moveItem = moveItem ? moveItem : this;
173 if (this->moveItem() == moveItem)
174 return;
175 d->m_moveItem = moveItem;
176 moveItemChanged();
177}
178
179/*!
180 * \qmlproperty bool QtWaylandCompositor::ShellSurfaceItem::autoCreatePopupItems
181 *
182 * This property holds whether ShellSurfaceItems for popups parented to the shell
183 * surface managed by this item should automatically be created.
184 */
185
186/*!
187 * \property QWaylandQuickShellSurfaceItem::autoCreatePopupItems
188 *
189 * This property holds whether QWaylandQuickShellSurfaceItems for popups
190 * parented to the shell surface managed by this item should automatically be created.
191 */
192bool QWaylandQuickShellSurfaceItem::autoCreatePopupItems()
193{
194 Q_D(const QWaylandQuickShellSurfaceItem);
195 return d->m_autoCreatePopupItems;
196}
197
198void QWaylandQuickShellSurfaceItem::setAutoCreatePopupItems(bool enabled)
199{
200 Q_D(QWaylandQuickShellSurfaceItem);
201
202 if (enabled == d->m_autoCreatePopupItems)
203 return;
204
205 d->m_autoCreatePopupItems = enabled;
206 emit autoCreatePopupItemsChanged();
207}
208
209/*!
210\class QWaylandQuickShellEventFilter
211\brief QWaylandQuickShellEventFilter implements a Wayland popup grab
212\internal
213*/
214
215void QWaylandQuickShellEventFilter::startFilter(QWaylandClient *client, CallbackFunction closePopups)
216{
217 if (!self)
218 self = new QWaylandQuickShellEventFilter(qGuiApp);
219 if (!self->eventFilterInstalled) {
220 qGuiApp->installEventFilter(filterObj: self);
221 self->eventFilterInstalled = true;
222 self->client = client;
223 self->closePopups = closePopups;
224 }
225}
226
227void QWaylandQuickShellEventFilter::cancelFilter()
228{
229 if (!self)
230 return;
231 if (self->eventFilterInstalled && !self->waitForRelease)
232 self->stopFilter();
233}
234
235void QWaylandQuickShellEventFilter::stopFilter()
236{
237 if (eventFilterInstalled) {
238 qGuiApp->removeEventFilter(obj: this);
239 eventFilterInstalled = false;
240 }
241}
242QWaylandQuickShellEventFilter *QWaylandQuickShellEventFilter::self = nullptr;
243
244QWaylandQuickShellEventFilter::QWaylandQuickShellEventFilter(QObject *parent)
245 : QObject(parent)
246{
247}
248
249bool QWaylandQuickShellEventFilter::eventFilter(QObject *receiver, QEvent *e)
250{
251 if (e->type() == QEvent::MouseButtonPress || e->type() == QEvent::MouseButtonRelease) {
252 bool press = e->type() == QEvent::MouseButtonPress;
253 if (press && !waitForRelease) {
254 // The user clicked something: we need to close popups unless this press is caught later
255 if (!mousePressTimeout.isActive())
256 mousePressTimeout.start(msec: 0, obj: this);
257 }
258
259 QQuickItem *item = qobject_cast<QQuickItem*>(object: receiver);
260 if (!item)
261 return false;
262
263 QMouseEvent *event = static_cast<QMouseEvent*>(e);
264 QWaylandQuickShellSurfaceItem *shellSurfaceItem = qobject_cast<QWaylandQuickShellSurfaceItem*>(object: item);
265 bool finalRelease = (event->type() == QEvent::MouseButtonRelease) && (event->buttons() == Qt::NoButton);
266 bool popupClient = shellSurfaceItem && shellSurfaceItem->surface() && shellSurfaceItem->surface()->client() == client;
267
268 if (waitForRelease) {
269 // We are eating events until all mouse buttons are released
270 if (finalRelease) {
271 waitForRelease = false;
272 stopFilter();
273 }
274 return true;
275 }
276
277 if (finalRelease && mousePressTimeout.isActive()) {
278 // the user somehow managed to press and release the mouse button in 0 milliseconds
279 qWarning(msg: "Badly written autotest detected");
280 mousePressTimeout.stop();
281 stopFilter();
282 }
283
284 if (press && !shellSurfaceItem && !QQmlProperty(item, QStringLiteral("qtwayland_blocking_overlay")).isValid()) {
285 // the user clicked on something that's not blocking mouse events
286 e->ignore(); //propagate the event to items below
287 return true; // don't give the event to the item
288 }
289
290 mousePressTimeout.stop(); // we've got this
291
292 if (press && !popupClient) {
293 // The user clicked outside the active popup's client. The popups should
294 // be closed, but the event filter will stay to catch the release-
295 // event before removing itself.
296 waitForRelease = true;
297 closePopups();
298 return true;
299 }
300 }
301
302 return false;
303}
304
305void QWaylandQuickShellEventFilter::timerEvent(QTimerEvent *event)
306{
307 if (event->timerId() == mousePressTimeout.timerId()) {
308 mousePressTimeout.stop();
309 closePopups();
310 stopFilter();
311 // Don't wait for release: Since the press wasn't accepted,
312 // the release won't be delivered.
313 }
314}
315
316QT_END_NAMESPACE
317

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