1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qwaylandwlshellsurface_p.h"
5
6#include <QtWaylandClient/private/qwaylanddisplay_p.h>
7#include <QtWaylandClient/private/qwaylandwindow_p.h>
8#include <QtWaylandClient/private/qwaylandinputdevice_p.h>
9#include <QtWaylandClient/private/qwaylandabstractdecoration_p.h>
10#include <QtWaylandClient/private/qwaylandscreen_p.h>
11#include <QtWaylandClient/private/qwaylandextendedsurface_p.h>
12
13#include <QtCore/QDebug>
14
15QT_BEGIN_NAMESPACE
16
17namespace QtWaylandClient {
18
19QWaylandWlShellSurface::QWaylandWlShellSurface(struct ::wl_shell_surface *shell_surface, QWaylandWindow *window)
20 : QWaylandShellSurface(window)
21 , QtWayland::wl_shell_surface(shell_surface)
22 , m_window(window)
23{
24 if (window->display()->windowExtension())
25 m_extendedWindow = new QWaylandExtendedSurface(window);
26
27 Qt::WindowType type = window->window()->type();
28 auto *transientParent = window->transientParent();
29 if (type == Qt::Popup && transientParent && transientParent->wlSurface())
30 setPopup(parent: transientParent, device: m_window->display()->lastInputDevice(), serial: m_window->display()->lastInputSerial());
31 else if (transientParent && transientParent->wlSurface())
32 updateTransientParent(parent: transientParent->window());
33 else
34 setTopLevel();
35}
36
37QWaylandWlShellSurface::~QWaylandWlShellSurface()
38{
39 wl_shell_surface_destroy(object());
40 delete m_extendedWindow;
41}
42
43bool QWaylandWlShellSurface::resize(QWaylandInputDevice *inputDevice, Qt::Edges edges)
44{
45 enum resize resizeEdges = convertToResizeEdges(edges);
46 resize(inputDevice: inputDevice->wl_seat(), edges: inputDevice->serial(), resizeEdges);
47 return true;
48}
49
50bool QWaylandWlShellSurface::move(QWaylandInputDevice *inputDevice)
51{
52 move(inputDevice: inputDevice->wl_seat(),
53 inputDevice->serial());
54 return true;
55}
56
57void QWaylandWlShellSurface::setTitle(const QString & title)
58{
59 return QtWayland::wl_shell_surface::set_title(title);
60}
61
62void QWaylandWlShellSurface::setAppId(const QString & appId)
63{
64 return QtWayland::wl_shell_surface::set_class(appId);
65}
66
67void QWaylandWlShellSurface::raise()
68{
69 if (m_extendedWindow)
70 m_extendedWindow->raise();
71}
72
73void QWaylandWlShellSurface::lower()
74{
75 if (m_extendedWindow)
76 m_extendedWindow->lower();
77}
78
79void QWaylandWlShellSurface::setContentOrientationMask(Qt::ScreenOrientations orientation)
80{
81 if (m_extendedWindow)
82 m_extendedWindow->setContentOrientationMask(orientation);
83}
84
85void QWaylandWlShellSurface::setWindowFlags(Qt::WindowFlags flags)
86{
87 if (m_extendedWindow)
88 m_extendedWindow->setWindowFlags(flags);
89}
90
91void QWaylandWlShellSurface::sendProperty(const QString &name, const QVariant &value)
92{
93 if (m_extendedWindow)
94 m_extendedWindow->updateGenericProperty(name, value);
95}
96
97void QWaylandWlShellSurface::applyConfigure()
98{
99 if ((m_pending.states & (Qt::WindowMaximized|Qt::WindowFullScreen))
100 && !(m_applied.states & (Qt::WindowMaximized|Qt::WindowFullScreen))) {
101 m_normalSize = m_window->windowFrameGeometry().size();
102 }
103
104 if (m_pending.states != m_applied.states)
105 m_window->handleWindowStatesChanged(m_pending.states);
106
107 if (!m_pending.size.isEmpty()) {
108 int x = 0;
109 int y = 0;
110 if (m_pending.edges & resize_left)
111 x = m_applied.size.width() - m_pending.size.width();
112 if (m_pending.edges & resize_top)
113 y = m_applied.size.height() - m_pending.size.height();
114 QPoint offset(x, y);
115 m_window->resizeFromApplyConfigure(m_pending.size, offset);
116 } else if (m_pending.size.isValid() && !m_normalSize.isEmpty()) {
117 m_window->resizeFromApplyConfigure(sizeWithMargins: m_normalSize);
118 }
119
120 m_applied = m_pending;
121}
122
123bool QWaylandWlShellSurface::wantsDecorations() const
124{
125 return !(m_pending.states & Qt::WindowFullScreen);
126}
127
128void QWaylandWlShellSurface::requestWindowStates(Qt::WindowStates states)
129{
130 // On wl-shell the client is in charge of states, so diff from the pending state
131 Qt::WindowStates changedStates = m_pending.states ^ states;
132 Qt::WindowStates addedStates = changedStates & states;
133
134 if (addedStates & Qt::WindowMinimized)
135 qCWarning(lcQpaWayland) << "Minimizing is not supported on wl-shell. Consider using xdg-shell instead.";
136
137 if (addedStates & Qt::WindowMaximized) {
138 set_maximized(nullptr);
139 m_window->applyConfigureWhenPossible();
140 }
141
142 if (addedStates & Qt::WindowFullScreen) {
143 set_fullscreen(WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 0, nullptr);
144 m_window->applyConfigureWhenPossible();
145 }
146
147 bool isNormal = !(states & Qt::WindowMaximized) && !(states & Qt::WindowFullScreen);
148 if (isNormal && (changedStates & (Qt::WindowMaximized | Qt::WindowFullScreen))) {
149 setTopLevel(); // set normal window
150 // There's usually no configure event after this, so just clear the rest of the pending
151 // configure here and queue the applyConfigure call
152 m_pending.size = {0, 0};
153 m_pending.edges = resize_none;
154 m_window->applyConfigureWhenPossible();
155 }
156
157 m_pending.states = states & ~Qt::WindowMinimized;
158}
159
160enum QWaylandWlShellSurface::resize QWaylandWlShellSurface::convertToResizeEdges(Qt::Edges edges)
161{
162 return static_cast<enum resize>(
163 ((edges & Qt::TopEdge) ? resize_top : 0)
164 | ((edges & Qt::BottomEdge) ? resize_bottom : 0)
165 | ((edges & Qt::LeftEdge) ? resize_left : 0)
166 | ((edges & Qt::RightEdge) ? resize_right : 0));
167}
168
169void QWaylandWlShellSurface::setTopLevel()
170{
171 set_toplevel();
172}
173
174static inline bool testShowWithoutActivating(const QWindow *window)
175{
176 // QWidget-attribute Qt::WA_ShowWithoutActivating.
177 const QVariant showWithoutActivating = window->property(name: "_q_showWithoutActivating");
178 return showWithoutActivating.isValid() && showWithoutActivating.toBool();
179}
180
181void QWaylandWlShellSurface::updateTransientParent(QWindow *parent)
182{
183 QWaylandWindow *parent_wayland_window = static_cast<QWaylandWindow *>(parent->handle());
184 if (!parent_wayland_window)
185 return;
186
187 // set_transient expects a position relative to the parent
188 QPoint transientPos = m_window->geometry().topLeft(); // this is absolute
189 transientPos -= parent->geometry().topLeft();
190 if (parent_wayland_window->decoration()) {
191 transientPos.setX(transientPos.x() + parent_wayland_window->decoration()->margins().left());
192 transientPos.setY(transientPos.y() + parent_wayland_window->decoration()->margins().top());
193 }
194
195 uint32_t flags = 0;
196 Qt::WindowFlags wf = m_window->window()->flags();
197 if (wf.testFlag(Qt::ToolTip)
198 || wf.testFlag(Qt::WindowTransparentForInput)
199 || testShowWithoutActivating(m_window->window()))
200 flags |= WL_SHELL_SURFACE_TRANSIENT_INACTIVE;
201
202 auto *parentSurface = parent_wayland_window->wlSurface();
203 Q_ASSERT(parentSurface);
204 set_transient(parentSurface, transientPos.x(), transientPos.y(), flags);
205}
206
207void QWaylandWlShellSurface::setPopup(QWaylandWindow *parent, QWaylandInputDevice *device, uint serial)
208{
209 QWaylandWindow *parent_wayland_window = parent;
210 if (!parent_wayland_window) {
211 qCWarning(lcQpaWayland) << "setPopup called without a parent window";
212 return;
213 }
214 if (!device) {
215 qCWarning(lcQpaWayland) << "setPopup called without an input device";
216 return;
217 }
218
219 // set_popup expects a position relative to the parent
220 QPoint transientPos = m_window->geometry().topLeft(); // this is absolute
221 transientPos -= parent_wayland_window->geometry().topLeft();
222 if (parent_wayland_window->decoration()) {
223 transientPos.setX(transientPos.x() + parent_wayland_window->decoration()->margins().left());
224 transientPos.setY(transientPos.y() + parent_wayland_window->decoration()->margins().top());
225 }
226
227 auto *parentSurface = parent_wayland_window->wlSurface();
228 Q_ASSERT(parentSurface);
229 uint flags = 0;
230 set_popup(device->wl_seat(), serial, parentSurface, transientPos.x(), transientPos.y(), flags);
231}
232
233void QWaylandWlShellSurface::shell_surface_ping(uint32_t serial)
234{
235 pong(serial);
236}
237
238void QWaylandWlShellSurface::shell_surface_configure(uint32_t edges, int32_t width, int32_t height)
239{
240 m_pending.size = QSize(width, height);
241 m_pending.edges = static_cast<enum resize>(edges);
242 if (m_pending.edges && !m_pending.size.isEmpty())
243 m_normalSize = m_pending.size;
244 m_window->applyConfigureWhenPossible();
245}
246
247void QWaylandWlShellSurface::shell_surface_popup_done()
248{
249 QCoreApplication::postEvent(receiver: m_window->window(), event: new QCloseEvent());
250}
251
252}
253
254QT_END_NAMESPACE
255
256#include "moc_qwaylandwlshellsurface_p.cpp"
257

source code of qtwayland/src/plugins/shellintegration/wl-shell/qwaylandwlshellsurface.cpp