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 | |
15 | QT_BEGIN_NAMESPACE |
16 | |
17 | namespace QtWaylandClient { |
18 | |
19 | QWaylandWlShellSurface::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 | |
37 | QWaylandWlShellSurface::~QWaylandWlShellSurface() |
38 | { |
39 | wl_shell_surface_destroy(object()); |
40 | delete m_extendedWindow; |
41 | } |
42 | |
43 | bool 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 | |
50 | bool QWaylandWlShellSurface::move(QWaylandInputDevice *inputDevice) |
51 | { |
52 | move(inputDevice: inputDevice->wl_seat(), |
53 | inputDevice->serial()); |
54 | return true; |
55 | } |
56 | |
57 | void QWaylandWlShellSurface::setTitle(const QString & title) |
58 | { |
59 | return QtWayland::wl_shell_surface::set_title(title); |
60 | } |
61 | |
62 | void QWaylandWlShellSurface::setAppId(const QString & appId) |
63 | { |
64 | return QtWayland::wl_shell_surface::set_class(appId); |
65 | } |
66 | |
67 | void QWaylandWlShellSurface::raise() |
68 | { |
69 | if (m_extendedWindow) |
70 | m_extendedWindow->raise(); |
71 | } |
72 | |
73 | void QWaylandWlShellSurface::lower() |
74 | { |
75 | if (m_extendedWindow) |
76 | m_extendedWindow->lower(); |
77 | } |
78 | |
79 | void QWaylandWlShellSurface::setContentOrientationMask(Qt::ScreenOrientations orientation) |
80 | { |
81 | if (m_extendedWindow) |
82 | m_extendedWindow->setContentOrientationMask(orientation); |
83 | } |
84 | |
85 | void QWaylandWlShellSurface::setWindowFlags(Qt::WindowFlags flags) |
86 | { |
87 | if (m_extendedWindow) |
88 | m_extendedWindow->setWindowFlags(flags); |
89 | } |
90 | |
91 | void QWaylandWlShellSurface::sendProperty(const QString &name, const QVariant &value) |
92 | { |
93 | if (m_extendedWindow) |
94 | m_extendedWindow->updateGenericProperty(name, value); |
95 | } |
96 | |
97 | void 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 | |
123 | bool QWaylandWlShellSurface::wantsDecorations() const |
124 | { |
125 | return !(m_pending.states & Qt::WindowFullScreen); |
126 | } |
127 | |
128 | void 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 | |
160 | enum 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 | |
169 | void QWaylandWlShellSurface::setTopLevel() |
170 | { |
171 | set_toplevel(); |
172 | } |
173 | |
174 | static 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 | |
181 | void 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 | |
207 | void 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 | |
233 | void QWaylandWlShellSurface::shell_surface_ping(uint32_t serial) |
234 | { |
235 | pong(serial); |
236 | } |
237 | |
238 | void 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 | |
247 | void QWaylandWlShellSurface::shell_surface_popup_done() |
248 | { |
249 | QCoreApplication::postEvent(receiver: m_window->window(), event: new QCloseEvent()); |
250 | } |
251 | |
252 | } |
253 | |
254 | QT_END_NAMESPACE |
255 | |
256 | #include "moc_qwaylandwlshellsurface_p.cpp" |
257 | |