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
qwaylandwlshellintegration.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
6
7#include <QtWaylandCompositor/QWaylandCompositor>
8#include <QtWaylandCompositor/QWaylandWlShellSurface>
9#include <QtWaylandCompositor/QWaylandQuickShellSurfaceItem>
10#include <QtWaylandCompositor/QWaylandSeat>
11
13
14namespace QtWayland {
15
16WlShellIntegration::WlShellIntegration(QWaylandQuickShellSurfaceItem *item)
17 : QWaylandQuickShellIntegration(item)
18 , m_item(item)
19 , m_shellSurface(qobject_cast<QWaylandWlShellSurface *>(item->shellSurface()))
20{
21 m_item->setSurface(m_shellSurface->surface());
22 connect(m_shellSurface.data(), &QWaylandWlShellSurface::startMove, this, &WlShellIntegration::handleStartMove);
23 connect(m_shellSurface.data(), &QWaylandWlShellSurface::startResize, this, &WlShellIntegration::handleStartResize);
24 connect(m_shellSurface->surface(), &QWaylandSurface::redraw, this, &WlShellIntegration::handleRedraw);
25 connect(m_shellSurface->surface(), &QWaylandSurface::offsetForNextFrame, this, &WlShellIntegration::adjustOffsetForNextFrame);
26 connect(m_shellSurface->surface(), &QWaylandSurface::hasContentChanged, this, &WlShellIntegration::handleSurfaceHasContentChanged);
27 connect(m_shellSurface.data(), &QWaylandWlShellSurface::setDefaultToplevel, this, &WlShellIntegration::handleSetDefaultTopLevel);
28 connect(m_shellSurface.data(), &QWaylandWlShellSurface::setTransient, this, &WlShellIntegration::handleSetTransient);
29 connect(m_shellSurface.data(), &QWaylandWlShellSurface::setMaximized, this, &WlShellIntegration::handleSetMaximized);
30 connect(m_shellSurface.data(), &QWaylandWlShellSurface::setFullScreen, this, &WlShellIntegration::handleSetFullScreen);
31 connect(m_shellSurface.data(), &QWaylandWlShellSurface::setPopup, this, &WlShellIntegration::handleSetPopup);
32 connect(m_shellSurface.data(), &QWaylandWlShellSurface::destroyed, this, &WlShellIntegration::handleShellSurfaceDestroyed);
33}
34
36{
37 m_item->setSurface(nullptr);
38}
39
40void WlShellIntegration::handleStartMove(QWaylandSeat *seat)
41{
42 grabberState = GrabberState::Move;
43 moveState.seat = seat;
44 moveState.initialized = false;
45}
46
47void WlShellIntegration::handleStartResize(QWaylandSeat *seat, QWaylandWlShellSurface::ResizeEdge edges)
48{
49 grabberState = GrabberState::Resize;
50 resizeState.seat = seat;
51 resizeState.resizeEdges = edges;
52 resizeState.initialSize = m_shellSurface->surface()->destinationSize();
53 resizeState.initialized = false;
54}
55
56void WlShellIntegration::handleSetDefaultTopLevel()
57{
58 // Take focus if the policy allows
59 if (m_shellSurface->shell()->focusPolicy() == QWaylandShell::AutomaticFocus)
60 m_item->takeFocus();
61
62 // In order to restore the window state, the client calls setDefaultToplevel()
63 // so we need to unset the flags here but we save the previous state and move
64 // to the initial position when redrawing
65 nextState = State::Windowed;
66
67 // Any handlers for making maximized or fullscreen state track the size of
68 // the designated output, are unneeded now that we're going to windowed
69 // state.
70 nonwindowedState.output = nullptr;
71 disconnect(nonwindowedState.sizeChangedConnection);
72}
73
74void WlShellIntegration::handleSetTransient(QWaylandSurface *parentSurface, const QPoint &relativeToParent, bool inactive)
75{
76 Q_UNUSED(parentSurface);
77 Q_UNUSED(relativeToParent);
78
79 // Take focus if the policy allows and it's not inactive
80 if (m_shellSurface->shell()->focusPolicy() == QWaylandShell::AutomaticFocus && !inactive)
81 m_item->takeFocus();
82}
83
84void WlShellIntegration::handleSetMaximized(QWaylandOutput *output)
85{
86 if (!m_item->view()->isPrimary())
87 return;
88
89 if (currentState == State::Maximized)
90 return;
91
92 QWaylandOutput *designatedOutput = output ? output : m_item->view()->output();
93 if (!designatedOutput)
94 return;
95
96 if (currentState == State::Windowed)
97 normalPosition = m_item->moveItem()->position();
98
99 nextState = State::Maximized;
100 finalPosition = designatedOutput->position() + designatedOutput->availableGeometry().topLeft();
101
102 // Any prior output-resize handlers are irrelevant at this point
103 disconnect(nonwindowedState.sizeChangedConnection);
104 nonwindowedState.output = designatedOutput;
105 nonwindowedState.sizeChangedConnection = connect(designatedOutput, &QWaylandOutput::availableGeometryChanged, this, &WlShellIntegration::handleMaximizedSizeChanged);
106 handleMaximizedSizeChanged();
107}
108
109void WlShellIntegration::handleMaximizedSizeChanged()
110{
111 if (!m_shellSurface)
112 return;
113
114 if (nextState == State::Maximized) {
115 QWaylandOutput *designatedOutput = nonwindowedState.output;
116 auto scaleFactor = designatedOutput->scaleFactor();
117 m_shellSurface->sendConfigure(designatedOutput->availableGeometry().size() / scaleFactor, QWaylandWlShellSurface::NoneEdge);
118 }
119}
120
121void WlShellIntegration::handleSetFullScreen(QWaylandWlShellSurface::FullScreenMethod method, uint framerate, QWaylandOutput *output)
122{
123 Q_UNUSED(method);
124 Q_UNUSED(framerate);
125
126 if (!m_item->view()->isPrimary())
127 return;
128
129 if (currentState == State::FullScreen)
130 return;
131
132 QWaylandOutput *designatedOutput = output ? output : m_item->view()->output();
133 if (!designatedOutput)
134 return;
135
136 if (currentState == State::Windowed)
137 normalPosition = m_item->moveItem()->position();
138
139 nextState = State::FullScreen;
140 finalPosition = designatedOutput->position();
141
142 // Any prior output-resize handlers are irrelevant at this point
143 disconnect(nonwindowedState.sizeChangedConnection);
144 nonwindowedState.output = designatedOutput;
145 nonwindowedState.sizeChangedConnection = connect(designatedOutput, &QWaylandOutput::geometryChanged, this, &WlShellIntegration::handleFullScreenSizeChanged);
146 handleFullScreenSizeChanged();
147}
148
149void WlShellIntegration::handleFullScreenSizeChanged()
150{
151 if (!m_shellSurface)
152 return;
153
154 if (nextState == State::FullScreen) {
155 QWaylandOutput *designatedOutput = nonwindowedState.output;
156 m_shellSurface->sendConfigure(designatedOutput->geometry().size(), QWaylandWlShellSurface::NoneEdge);
157 }
158}
159
160void WlShellIntegration::handleSetPopup(QWaylandSeat *seat, QWaylandSurface *parent, const QPoint &relativeToParent)
161{
162 Q_UNUSED(seat);
163
164 // Find the parent item on the same output
165 QWaylandQuickShellSurfaceItem *parentItem = nullptr;
166 const auto views = parent->views();
167 for (QWaylandView *view : views) {
168 if (view->output() == m_item->view()->output()) {
169 QWaylandQuickShellSurfaceItem *item = qobject_cast<QWaylandQuickShellSurfaceItem*>(view->renderObject());
170 if (item) {
171 parentItem = item;
172 break;
173 }
174 }
175 }
176
177 if (parentItem) {
178 // Clear all the transforms for this ShellSurfaceItem. They are not
179 // applicable when the item becomes a child to a surface that has its
180 // own transforms. Otherwise the transforms would be applied twice.
181 QQmlListProperty<QQuickTransform> t = m_item->transform();
182 t.clear(&t);
183 m_item->setRotation(0);
184 m_item->setScale(1.0);
185 m_item->setPosition(m_item->mapFromSurface(relativeToParent));
186 m_item->setParentItem(parentItem);
187 }
188
189 isPopup = true;
190 auto shell = m_shellSurface->shell();
191 QWaylandQuickShellEventFilter::startFilter(m_shellSurface->surface()->client(), [shell]() {
192 shell->closeAllPopups();
193 });
194
195 QObject::connect(m_shellSurface->surface(), &QWaylandSurface::hasContentChanged,
196 this, &WlShellIntegration::handleSurfaceHasContentChanged);
197}
198
199void WlShellIntegration::handlePopupClosed()
200{
201 handlePopupRemoved();
202 if (m_shellSurface)
203 QObject::disconnect(m_shellSurface->surface(), &QWaylandSurface::hasContentChanged,
204 this, &WlShellIntegration::handleSurfaceHasContentChanged);
205}
206
207void WlShellIntegration::handlePopupRemoved()
208{
209 if (!m_shellSurface || m_shellSurface->shell()->mappedPopups().isEmpty())
210 QWaylandQuickShellEventFilter::cancelFilter();
211 isPopup = false;
212}
213
214qreal WlShellIntegration::devicePixelRatio() const
215{
216 return m_item->window() ? m_item->window()->devicePixelRatio() : 1;
217}
218
219void WlShellIntegration::handleShellSurfaceDestroyed()
220{
221 if (isPopup)
222 handlePopupRemoved();
223
224 // Disarm any handlers that might fire and attempt to use the now-stale pointer
225 nonwindowedState.output = nullptr;
226 disconnect(nonwindowedState.sizeChangedConnection);
227
228 m_shellSurface = nullptr;
229}
230
231void WlShellIntegration::handleSurfaceHasContentChanged()
232{
233 if (m_shellSurface && m_shellSurface->surface()->destinationSize().isEmpty()
234 && m_shellSurface->windowType() == Qt::WindowType::Popup) {
235 handlePopupClosed();
236 }
237}
238
239void WlShellIntegration::handleRedraw()
240{
241 if (currentState == nextState)
242 return;
243
244 m_item->moveItem()->setPosition(nextState == State::Windowed ? normalPosition : finalPosition);
245 currentState = nextState;
246}
247
248void WlShellIntegration::adjustOffsetForNextFrame(const QPointF &offset)
249{
250 if (!m_item->view()->isPrimary())
251 return;
252
253 QQuickItem *moveItem = m_item->moveItem();
254 moveItem->setPosition(moveItem->position() + m_item->mapFromSurface(offset));
255}
256
257bool WlShellIntegration::eventFilter(QObject *object, QEvent *event)
258{
259 if (event->type() == QEvent::MouseMove) {
260 QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
261 return filterMouseMoveEvent(mouseEvent);
262 } else if (event->type() == QEvent::MouseButtonRelease) {
263 QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
264 return filterMouseReleaseEvent(mouseEvent);
265 }
266 return QWaylandQuickShellIntegration::eventFilter(object, event);
267}
268
269bool WlShellIntegration::filterMouseMoveEvent(QMouseEvent *event)
270{
271 if (grabberState == GrabberState::Resize) {
272 Q_ASSERT(resizeState.seat == m_item->compositor()->seatFor(event));
273 if (!resizeState.initialized) {
274 resizeState.initialMousePos = event->scenePosition();
275 resizeState.initialized = true;
276 return true;
277 }
278 float scaleFactor = m_item->view()->output()->scaleFactor();
279 QPointF delta = (event->scenePosition() - resizeState.initialMousePos) / scaleFactor * devicePixelRatio();
280 QSize newSize = m_shellSurface->sizeForResize(resizeState.initialSize, delta, resizeState.resizeEdges);
281 m_shellSurface->sendConfigure(newSize, resizeState.resizeEdges);
282 } else if (grabberState == GrabberState::Move) {
283 Q_ASSERT(moveState.seat == m_item->compositor()->seatFor(event));
284 QQuickItem *moveItem = m_item->moveItem();
285 if (!moveState.initialized) {
286 moveState.initialOffset = moveItem->mapFromItem(nullptr, event->scenePosition());
287 moveState.initialized = true;
288 return true;
289 }
290 if (!moveItem->parentItem())
291 return true;
292 QPointF parentPos = moveItem->parentItem()->mapFromItem(nullptr, event->scenePosition());
293 moveItem->setPosition(parentPos - moveState.initialOffset);
294 }
295 return false;
296}
297
298bool WlShellIntegration::filterMouseReleaseEvent(QMouseEvent *event)
299{
300 Q_UNUSED(event);
301 if (grabberState != GrabberState::Default) {
302 grabberState = GrabberState::Default;
303 return true;
304 }
305 return false;
306}
307
308}
309
310QT_END_NAMESPACE
311
312#include "moc_qwaylandwlshellintegration_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.
Combined button and popup list for selecting options.