Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qwaylandxdgshellintegration.cpp
Go to the documentation of this file.
1// Copyright (C) 2018 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
5
6#include <QtWaylandCompositor/QWaylandXdgSurface>
7#include <QtWaylandCompositor/QWaylandCompositor>
8#include <QtWaylandCompositor/QWaylandSeat>
9
11
12namespace QtWayland {
13
14static void handlePopupCreated(QWaylandQuickShellSurfaceItem *parentItem, QWaylandXdgPopup *popup)
15{
16 if (parentItem->shellSurface() == popup->parentXdgSurface())
17 QWaylandQuickShellSurfaceItemPrivate::get(parentItem)->maybeCreateAutoPopup(popup->xdgSurface());
18}
19
20XdgToplevelIntegration::XdgToplevelIntegration(QWaylandQuickShellSurfaceItem *item)
21 : QWaylandQuickShellIntegration(item)
22 , m_item(item)
23 , m_xdgSurface(qobject_cast<QWaylandXdgSurface *>(item->shellSurface()))
24 , m_toplevel(m_xdgSurface->toplevel())
25 , grabberState(GrabberState::Default)
26{
27 Q_ASSERT(m_toplevel);
28
29 m_item->setSurface(m_xdgSurface->surface());
30
31 connect(m_toplevel, &QWaylandXdgToplevel::startMove, this, &XdgToplevelIntegration::handleStartMove);
32 connect(m_toplevel, &QWaylandXdgToplevel::startResize, this, &XdgToplevelIntegration::handleStartResize);
33 connect(m_toplevel, &QWaylandXdgToplevel::setMaximized, this, &XdgToplevelIntegration::handleSetMaximized);
34 connect(m_toplevel, &QWaylandXdgToplevel::unsetMaximized, this, &XdgToplevelIntegration::handleUnsetMaximized);
35 connect(m_toplevel, &QWaylandXdgToplevel::maximizedChanged, this, &XdgToplevelIntegration::handleMaximizedChanged);
36 connect(m_toplevel, &QWaylandXdgToplevel::setFullscreen, this, &XdgToplevelIntegration::handleSetFullscreen);
37 connect(m_toplevel, &QWaylandXdgToplevel::unsetFullscreen, this, &XdgToplevelIntegration::handleUnsetFullscreen);
38 connect(m_toplevel, &QWaylandXdgToplevel::fullscreenChanged, this, &XdgToplevelIntegration::handleFullscreenChanged);
39 connect(m_toplevel, &QWaylandXdgToplevel::activatedChanged, this, &XdgToplevelIntegration::handleActivatedChanged);
40 connect(m_xdgSurface->shell(), &QWaylandXdgShell::popupCreated, this, [item](QWaylandXdgPopup *popup, QWaylandXdgSurface *){
41 handlePopupCreated(item, popup);
42 });
43 connect(m_xdgSurface->surface(), &QWaylandSurface::destinationSizeChanged, this, &XdgToplevelIntegration::handleSurfaceSizeChanged);
44 connect(m_toplevel, &QObject::destroyed, this, &XdgToplevelIntegration::handleToplevelDestroyed);
45}
46
47bool XdgToplevelIntegration::eventFilter(QObject *object, QEvent *event)
48{
49 if (event->type() == QEvent::MouseMove) {
50 QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
51 return filterMouseMoveEvent(mouseEvent);
52 } else if (event->type() == QEvent::MouseButtonRelease || event->type() == QEvent::TouchEnd || event->type() == QEvent::TouchCancel) {
53 return filterPointerReleaseEvent();
54 } else if (event->type() == QEvent::TouchUpdate) {
55 QTouchEvent *touchEvent = static_cast<QTouchEvent *>(event);
56 return filterTouchUpdateEvent(touchEvent);
57 }
58 return QWaylandQuickShellIntegration::eventFilter(object, event);
59}
60
61bool XdgToplevelIntegration::filterPointerMoveEvent(const QPointF &scenePosition)
62{
63 if (grabberState == GrabberState::Resize) {
64 if (!resizeState.initialized) {
65 resizeState.initialMousePos = scenePosition;
66 resizeState.initialized = true;
67 return true;
68 }
69 QPointF delta = m_item->mapToSurface(scenePosition - resizeState.initialMousePos);
70 QSize newSize = m_toplevel->sizeForResize(resizeState.initialWindowSize, delta, resizeState.resizeEdges);
71 m_toplevel->sendResizing(newSize);
72 } else if (grabberState == GrabberState::Move) {
73 QQuickItem *moveItem = m_item->moveItem();
74 if (!moveState.initialized) {
75 moveState.initialOffset = moveItem->mapFromItem(nullptr, scenePosition);
76 moveState.initialized = true;
77 return true;
78 }
79 if (!moveItem->parentItem())
80 return true;
81 QPointF parentPos = moveItem->parentItem()->mapFromItem(nullptr, scenePosition);
82 moveItem->setPosition(parentPos - moveState.initialOffset);
83 }
84 return false;
85}
86
87bool XdgToplevelIntegration::filterTouchUpdateEvent(QTouchEvent *event)
88{
89 if (event->pointCount() == 0)
90 return false;
91
92 Q_ASSERT(grabberState != GrabberState::Move || moveState.seat == m_item->compositor()->seatFor(event));
93 Q_ASSERT(grabberState != GrabberState::Resize || resizeState.seat == m_item->compositor()->seatFor(event));
94
95 QEventPoint point = event->points().first();
96 return filterPointerMoveEvent(point.scenePosition());
97 }
98
99bool XdgToplevelIntegration::filterMouseMoveEvent(QMouseEvent *event)
100{
101 Q_ASSERT(grabberState != GrabberState::Move || moveState.seat == m_item->compositor()->seatFor(event));
102 Q_ASSERT(grabberState != GrabberState::Resize || resizeState.seat == m_item->compositor()->seatFor(event));
103
104 return filterPointerMoveEvent(event->scenePosition());
105}
106
107bool XdgToplevelIntegration::filterPointerReleaseEvent()
108{
109 if (grabberState != GrabberState::Default) {
110 grabberState = GrabberState::Default;
111 return true;
112 }
113 return false;
114}
115
116void XdgToplevelIntegration::handleStartMove(QWaylandSeat *seat)
117{
118 grabberState = GrabberState::Move;
119 moveState.seat = seat;
120 moveState.initialized = false;
121}
122
123void XdgToplevelIntegration::handleStartResize(QWaylandSeat *seat, Qt::Edges edges)
124{
125 grabberState = GrabberState::Resize;
126 resizeState.seat = seat;
127 resizeState.resizeEdges = edges;
128 resizeState.initialWindowSize = m_xdgSurface->windowGeometry().size();
129 resizeState.initialPosition = m_item->moveItem()->position();
130 resizeState.initialSurfaceSize = m_item->surface()->destinationSize();
131 resizeState.initialized = false;
132}
133
134void XdgToplevelIntegration::handleSetMaximized()
135{
136 if (!m_item->view()->isPrimary())
137 return;
138
139 QList<QWaylandXdgToplevel::State> states = m_toplevel->states();
140
141 if (!states.contains(QWaylandXdgToplevel::State::FullscreenState) && !states.contains(QWaylandXdgToplevel::State::MaximizedState)) {
142 windowedGeometry.initialWindowSize = m_xdgSurface->windowGeometry().size();
143 windowedGeometry.initialPosition = m_item->moveItem()->position();
144 }
145
146 // Any prior output-resize handlers are irrelevant at this point.
147 disconnect(nonwindowedState.sizeChangedConnection);
148 nonwindowedState.output = m_item->view()->output();
149 nonwindowedState.sizeChangedConnection = connect(nonwindowedState.output, &QWaylandOutput::availableGeometryChanged, this, &XdgToplevelIntegration::handleMaximizedSizeChanged);
150 handleMaximizedSizeChanged();
151}
152
153void XdgToplevelIntegration::handleMaximizedSizeChanged()
154{
155 // Insurance against handleToplevelDestroyed() not managing to disconnect this
156 // handler in time.
157 if (m_toplevel == nullptr)
158 return;
159
160 m_toplevel->sendMaximized(nonwindowedState.output->availableGeometry().size() / nonwindowedState.output->scaleFactor());
161}
162
163void XdgToplevelIntegration::handleUnsetMaximized()
164{
165 if (!m_item->view()->isPrimary())
166 return;
167
168 // If no prior windowed size was recorded, send a 0x0 configure event
169 // to allow the client to choose its preferred size.
170 if (windowedGeometry.initialWindowSize.isValid())
171 m_toplevel->sendUnmaximized(windowedGeometry.initialWindowSize);
172 else
173 m_toplevel->sendUnmaximized();
174}
175
176void XdgToplevelIntegration::handleMaximizedChanged()
177{
178 if (m_toplevel->maximized()) {
179 if (auto *output = m_item->view()->output()) {
180 m_item->moveItem()->setPosition(output->position() + output->availableGeometry().topLeft());
181 } else {
182 qCWarning(qLcWaylandCompositor) << "The view does not have a corresponding output,"
183 << "ignoring maximized state";
184 }
185 } else {
186 m_item->moveItem()->setPosition(windowedGeometry.initialPosition);
187 }
188}
189
190void XdgToplevelIntegration::handleSetFullscreen()
191{
192 if (!m_item->view()->isPrimary())
193 return;
194
195 QList<QWaylandXdgToplevel::State> states = m_toplevel->states();
196
197 if (!states.contains(QWaylandXdgToplevel::State::FullscreenState) && !states.contains(QWaylandXdgToplevel::State::MaximizedState)) {
198 windowedGeometry.initialWindowSize = m_xdgSurface->windowGeometry().size();
199 windowedGeometry.initialPosition = m_item->moveItem()->position();
200 }
201
202 // Any prior output-resize handlers are irrelevant at this point.
203 disconnect(nonwindowedState.sizeChangedConnection);
204 nonwindowedState.output = m_item->view()->output();
205 nonwindowedState.sizeChangedConnection = connect(nonwindowedState.output, &QWaylandOutput::geometryChanged, this, &XdgToplevelIntegration::handleFullscreenSizeChanged);
206 handleFullscreenSizeChanged();
207}
208
209void XdgToplevelIntegration::handleFullscreenSizeChanged()
210{
211 // Insurance against handleToplevelDestroyed() not managing to disconnect this
212 // handler in time.
213 if (m_toplevel == nullptr)
214 return;
215
216 m_toplevel->sendFullscreen(nonwindowedState.output->geometry().size() / nonwindowedState.output->scaleFactor());
217}
218
219void XdgToplevelIntegration::handleUnsetFullscreen()
220{
221 if (!m_item->view()->isPrimary())
222 return;
223
224 // If no prior windowed size was recorded, send a 0x0 configure event
225 // to allow the client to choose its preferred size.
226 if (windowedGeometry.initialWindowSize.isValid())
227 m_toplevel->sendUnmaximized(windowedGeometry.initialWindowSize);
228 else
229 m_toplevel->sendUnmaximized();
230}
231
232void XdgToplevelIntegration::handleFullscreenChanged()
233{
234 if (m_toplevel->fullscreen()) {
235 if (auto *output = m_item->view()->output()) {
236 m_item->moveItem()->setPosition(output->position() + output->geometry().topLeft());
237 } else {
238 qCWarning(qLcWaylandCompositor) << "The view does not have a corresponding output,"
239 << "ignoring fullscreen state";
240 }
241 } else {
242 m_item->moveItem()->setPosition(windowedGeometry.initialPosition);
243 }
244}
245
246void XdgToplevelIntegration::handleActivatedChanged()
247{
248 if (m_toplevel->activated())
249 m_item->raise();
250}
251
252void XdgToplevelIntegration::handleSurfaceSizeChanged()
253{
254 if (grabberState == GrabberState::Resize) {
255 qreal dx = 0;
256 qreal dy = 0;
257 if (resizeState.resizeEdges & Qt::TopEdge)
258 dy = resizeState.initialSurfaceSize.height() - m_item->surface()->destinationSize().height();
259 if (resizeState.resizeEdges & Qt::LeftEdge)
260 dx = resizeState.initialSurfaceSize.width() - m_item->surface()->destinationSize().width();
261 QPointF offset = m_item->mapFromSurface({dx, dy});
262 m_item->moveItem()->setPosition(resizeState.initialPosition + offset);
263 }
264}
265
266void XdgToplevelIntegration::handleToplevelDestroyed()
267{
268 // Disarm any handlers that might fire on the now-stale toplevel pointer
269 nonwindowedState.output = nullptr;
270 disconnect(nonwindowedState.sizeChangedConnection);
271}
272
273XdgPopupIntegration::XdgPopupIntegration(QWaylandQuickShellSurfaceItem *item)
274 : m_item(item)
275 , m_xdgSurface(qobject_cast<QWaylandXdgSurface *>(item->shellSurface()))
276 , m_popup(m_xdgSurface->popup())
277{
278 Q_ASSERT(m_popup);
279
280 m_item->setSurface(m_xdgSurface->surface());
281 handleGeometryChanged();
282
283 connect(m_popup, &QWaylandXdgPopup::configuredGeometryChanged, this, &XdgPopupIntegration::handleGeometryChanged);
284 connect(m_xdgSurface->shell(), &QWaylandXdgShell::popupCreated, this, [item](QWaylandXdgPopup *popup, QWaylandXdgSurface *){
285 handlePopupCreated(item, popup);
286 });
287}
288
289void XdgPopupIntegration::handleGeometryChanged()
290{
291 if (m_item->view()->output()) {
292 const QPoint windowOffset = m_popup->parentXdgSurface()->windowGeometry().topLeft();
293 const QPoint surfacePosition = m_popup->unconstrainedPosition() + windowOffset;
294 const QPoint itemPosition = m_item->mapFromSurface(surfacePosition).toPoint();
295 //TODO: positioner size or other size...?
296 //TODO check positioner constraints etc... sliding, flipping
297 m_item->moveItem()->setPosition(itemPosition);
298 } else {
299 qWarning() << "XdgPopupIntegration popup item without output" << m_item;
300 }
301}
302
303}
304
305QT_END_NAMESPACE
306
307#include "moc_qwaylandxdgshellintegration_p.cpp"
bool eventFilter(QObject *object, QEvent *event) override
Filters events if this object has been installed as an event filter for the watched object.
static void handlePopupCreated(QWaylandQuickShellSurfaceItem *parentItem, QWaylandXdgPopup *popup)