GCC Code Coverage Report


Directory: ./
File: server/xdg_shell_toplevel.cpp
Date: 2024-01-22 17:25:27
Exec Total Coverage
Lines: 233 247 94.3%
Branches: 63 83 75.9%

Line Branch Exec Source
1 /****************************************************************************
2 Copyright © 2020 Roman Gilg <subdiff@gmail.com>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 2.1 of the License, or (at your option) version 3, or any
8 later version accepted by the membership of KDE e.V. (or its
9 successor approved by the membership of KDE e.V.), which shall
10 act as a proxy defined in Section 6 of version 3 of the license.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 ****************************************************************************/
20 #include "xdg_shell_toplevel.h"
21 #include "xdg_shell_toplevel_p.h"
22
23 #include "display.h"
24 #include "logging.h"
25 #include "seat_p.h"
26 #include "wl_output_p.h"
27 #include "xdg_shell_surface.h"
28 #include "xdg_shell_surface_p.h"
29
30 #include "wayland/global.h"
31
32 namespace Wrapland::Server
33 {
34
35 const struct xdg_toplevel_interface XdgShellToplevel::Private::s_interface = {
36 destroyCallback,
37 setParentCallback,
38 setTitleCallback,
39 setAppIdCallback,
40 showWindowMenuCallback,
41 moveCallback,
42 resizeCallback,
43 setMaxSizeCallback,
44 setMinSizeCallback,
45 setMaximizedCallback,
46 unsetMaximizedCallback,
47 setFullscreenCallback,
48 unsetFullscreenCallback,
49 setMinimizedCallback,
50 };
51 69
52 138 XdgShellToplevel::Private::Private(uint32_t version,
53 uint32_t id,
54 XdgShellSurface* surface,
55 XdgShellToplevel* q_ptr)
56 138 : Wayland::Resource<XdgShellToplevel>(surface->d_ptr->client,
57 69 version,
58 69 id,
59 &xdg_toplevel_interface,
60 &s_interface,
61 69 q_ptr)
62 69 , shellSurface{surface}
63 69 {
64 69 }
65
66 1 void XdgShellToplevel::Private::setTitleCallback([[maybe_unused]] wl_client* wlClient,
67 wl_resource* wlResource,
68 char const* title)
69 {
70 1 auto priv = get_handle(wlResource)->d_ptr;
71
72
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (priv->title == title) {
73 return;
74 }
75
76 1 priv->title = title;
77
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 Q_EMIT priv->handle->titleChanged(title);
78 1 }
79
80 1 void XdgShellToplevel::Private::setAppIdCallback([[maybe_unused]] wl_client* wlClient,
81 wl_resource* wlResource,
82 char const* app_id)
83 {
84 1 auto priv = get_handle(wlResource)->d_ptr;
85
86
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (priv->appId == app_id) {
87 return;
88 }
89
90 1 priv->appId = app_id;
91
2/4
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
1 Q_EMIT priv->handle->appIdChanged(app_id);
92 8 }
93 7
94 1 void XdgShellToplevel::Private::moveCallback([[maybe_unused]] wl_client* wlClient,
95 wl_resource* wlResource,
96 wl_resource* wlSeat,
97 uint32_t serial)
98 {
99 1 auto priv = get_handle(wlResource)->d_ptr;
100 1 Q_EMIT priv->handle->moveRequested(SeatGlobal::get_handle(wlSeat), serial);
101 1 }
102
103 9 Qt::Edges edgesToQtEdges(xdg_toplevel_resize_edge edges)
104 {
105 9 Qt::Edges qtEdges;
106
9/10
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 1 times.
✓ Branch 5 taken 1 times.
✓ Branch 6 taken 1 times.
✓ Branch 7 taken 1 times.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
9 switch (edges) {
107 case XDG_TOPLEVEL_RESIZE_EDGE_TOP:
108 1 qtEdges = Qt::TopEdge;
109 1 break;
110 case XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM:
111 1 qtEdges = Qt::BottomEdge;
112 1 break;
113 case XDG_TOPLEVEL_RESIZE_EDGE_LEFT:
114 1 qtEdges = Qt::LeftEdge;
115 1 break;
116 case XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT:
117 1 qtEdges = Qt::TopEdge | Qt::LeftEdge;
118 1 break;
119 case XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT:
120 1 qtEdges = Qt::BottomEdge | Qt::LeftEdge;
121 1 break;
122 case XDG_TOPLEVEL_RESIZE_EDGE_RIGHT:
123 1 qtEdges = Qt::RightEdge;
124 1 break;
125 case XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT:
126 1 qtEdges = Qt::TopEdge | Qt::RightEdge;
127 1 break;
128 case XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT:
129 1 qtEdges = Qt::BottomEdge | Qt::RightEdge;
130 1 break;
131 case XDG_TOPLEVEL_RESIZE_EDGE_NONE:
132 1 break;
133 default:
134 Q_UNREACHABLE();
135 break;
136 }
137 9 return qtEdges;
138 }
139
140 9 void XdgShellToplevel::Private::resizeCallback([[maybe_unused]] wl_client* wlClient,
141 wl_resource* wlResource,
142 wl_resource* wlSeat,
143 uint32_t serial,
144 uint32_t edges)
145 {
146 9 auto priv = get_handle(wlResource)->d_ptr;
147 18 Q_EMIT priv->handle->resizeRequested(
148 9 SeatGlobal::get_handle(wlSeat),
149 9 serial,
150 9 edgesToQtEdges(static_cast<xdg_toplevel_resize_edge>(edges)));
151 9 }
152
153 30 void XdgShellToplevel::Private::ackConfigure(uint32_t serial)
154 {
155 30 auto& serials = shellSurface->d_ptr->configureSerials;
156
1/2
✓ Branch 0 taken 30 times.
✗ Branch 1 not taken.
30 if (std::count(serials.cbegin(), serials.cend(), serial) == 0) {
157 return;
158 }
159 32 for (;;) {
160
1/2
✓ Branch 0 taken 32 times.
✗ Branch 1 not taken.
32 if (serials.empty()) {
161 break;
162 }
163 32 auto next_serial = serials.front();
164 32 serials.pop_front();
165
166 32 Q_EMIT handle->configureAcknowledged(next_serial);
167
2/2
✓ Branch 0 taken 30 times.
✓ Branch 1 taken 2 times.
32 if (next_serial == serial) {
168 30 break;
169 }
170 }
171 30 }
172
173 39 uint32_t XdgShellToplevel::Private::configure(XdgShellSurface::States states, QSize const& size)
174 {
175 39 const uint32_t serial = client->display()->handle->nextSerial();
176
177 wl_array configureStates;
178 39 wl_array_init(&configureStates);
179
180
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 8 times.
39 if (states.testFlag(XdgShellSurface::State::Maximized)) {
181 8 auto state = static_cast<uint32_t*>(wl_array_add(&configureStates, sizeof(uint32_t)));
182 8 *state = XDG_TOPLEVEL_STATE_MAXIMIZED;
183 8 }
184
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 7 times.
39 if (states.testFlag(XdgShellSurface::State::Fullscreen)) {
185 7 auto state = static_cast<uint32_t*>(wl_array_add(&configureStates, sizeof(uint32_t)));
186 7 *state = XDG_TOPLEVEL_STATE_FULLSCREEN;
187 7 }
188
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 7 times.
39 if (states.testFlag(XdgShellSurface::State::Resizing)) {
189 7 auto state = static_cast<uint32_t*>(wl_array_add(&configureStates, sizeof(uint32_t)));
190 7 *state = XDG_TOPLEVEL_STATE_RESIZING;
191 7 }
192
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 7 times.
39 if (states.testFlag(XdgShellSurface::State::Activated)) {
193 7 auto state = static_cast<uint32_t*>(wl_array_add(&configureStates, sizeof(uint32_t)));
194 7 *state = XDG_TOPLEVEL_STATE_ACTIVATED;
195 7 }
196
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 4 times.
39 if (states.testFlag(XdgShellSurface::State::TiledLeft)) {
197 4 auto state = static_cast<uint32_t*>(wl_array_add(&configureStates, sizeof(uint32_t)));
198 4 *state = XDG_TOPLEVEL_STATE_TILED_LEFT;
199 4 }
200
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 4 times.
39 if (states.testFlag(XdgShellSurface::State::TiledRight)) {
201 4 auto state = static_cast<uint32_t*>(wl_array_add(&configureStates, sizeof(uint32_t)));
202 4 *state = XDG_TOPLEVEL_STATE_TILED_RIGHT;
203 4 }
204
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 4 times.
39 if (states.testFlag(XdgShellSurface::State::TiledTop)) {
205 4 auto state = static_cast<uint32_t*>(wl_array_add(&configureStates, sizeof(uint32_t)));
206 4 *state = XDG_TOPLEVEL_STATE_TILED_TOP;
207 4 }
208
2/2
✓ Branch 0 taken 35 times.
✓ Branch 1 taken 4 times.
39 if (states.testFlag(XdgShellSurface::State::TiledBottom)) {
209 4 auto state = static_cast<uint32_t*>(wl_array_add(&configureStates, sizeof(uint32_t)));
210 4 *state = XDG_TOPLEVEL_STATE_TILED_BOTTOM;
211 4 }
212
213 39 shellSurface->d_ptr->configureSerials.push_back(serial);
214
215 39 send<xdg_toplevel_send_configure>(
216 39 std::max(size.width(), 0), std::max(size.height(), 0), &configureStates);
217 39 shellSurface->d_ptr->send<xdg_surface_send_configure>(serial);
218
219 39 client->flush();
220 39 wl_array_release(&configureStates);
221 39 return serial;
222 }
223
224 2 QSize XdgShellToplevel::Private::minimumSize() const
225 {
226 2 return m_currentState.minimumSize;
227 }
228
229 2 QSize XdgShellToplevel::Private::maximumSize() const
230 {
231 2 return m_currentState.maximiumSize;
232 }
233
234 1 void XdgShellToplevel::Private::close()
235 {
236 1 send<xdg_toplevel_send_close>();
237 1 client->flush();
238 1 }
239
240 7 void XdgShellToplevel::Private::commit()
241 {
242 7 bool const minimumSizeChanged = m_pendingState.minimumSizeIsSet;
243 7 bool const maximumSizeChanged = m_pendingState.maximumSizeIsSet;
244
245
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 2 times.
7 if (minimumSizeChanged) {
246 2 m_currentState.minimumSize = m_pendingState.minimumSize;
247 2 }
248
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 2 times.
7 if (maximumSizeChanged) {
249 2 m_currentState.maximiumSize = m_pendingState.maximiumSize;
250 2 }
251
252 21 m_pendingState = ShellSurfaceState{};
253
254
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 2 times.
7 if (minimumSizeChanged) {
255 2 Q_EMIT handle->minSizeChanged(m_currentState.minimumSize);
256 2 }
257
2/2
✓ Branch 0 taken 5 times.
✓ Branch 1 taken 2 times.
7 if (maximumSizeChanged) {
258 2 Q_EMIT handle->maxSizeChanged(m_currentState.maximiumSize);
259 2 }
260 7 }
261
262 2 void XdgShellToplevel::Private::setMaxSizeCallback([[maybe_unused]] wl_client* wlClient,
263 wl_resource* wlResource,
264 int32_t width,
265 int32_t height)
266 {
267 2 auto priv = get_handle(wlResource)->d_ptr;
268
269
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 2 times.
2 if (width < 0 || height < 0) {
270 priv->postError(XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE,
271 "Invalid xdg-toplevel maximum size");
272 return;
273 }
274
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (width == 0) {
275 width = INT32_MAX;
276 }
277
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (height == 0) {
278 height = INT32_MAX;
279 }
280
281 2 priv->m_pendingState.maximiumSize = QSize(width, height);
282 2 priv->m_pendingState.maximumSizeIsSet = true;
283 2 }
284
285 2 void XdgShellToplevel::Private::setMinSizeCallback([[maybe_unused]] wl_client* wlClient,
286 wl_resource* wlResource,
287 int32_t width,
288 int32_t height)
289 {
290 2 auto priv = get_handle(wlResource)->d_ptr;
291
292
2/4
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
✗ Branch 3 not taken.
2 if (width < 0 || height < 0) {
293 priv->postError(XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE,
294 "Invalid xdg-toplevel minimum size");
295 return;
296 }
297
298 2 priv->m_pendingState.minimumSize = QSize(width, height);
299 2 priv->m_pendingState.minimumSizeIsSet = true;
300 2 }
301
302 2 void XdgShellToplevel::Private::setParentCallback([[maybe_unused]] wl_client* wlClient,
303 wl_resource* wlResource,
304 wl_resource* wlParent)
305 {
306 2 auto priv = get_handle(wlResource)->d_ptr;
307
308
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (!wlParent) {
309 // setting null is valid API. Clear
310 1 priv->parentSurface = nullptr;
311 1 Q_EMIT priv->handle->transientForChanged();
312 1 } else {
313 1 auto parent = Wayland::Resource<XdgShellToplevel>::get_handle(wlParent);
314
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if (priv->parentSurface != parent) {
315 1 priv->parentSurface = parent;
316 1 Q_EMIT priv->handle->transientForChanged();
317 1 }
318 }
319 2 }
320
321 1 void XdgShellToplevel::Private::showWindowMenuCallback([[maybe_unused]] wl_client* wlClient,
322 wl_resource* wlResource,
323 wl_resource* wlSeat,
324 uint32_t serial,
325 int32_t pos_x,
326 int32_t pos_y)
327 {
328 1 auto priv = get_handle(wlResource)->d_ptr;
329 1 auto seat = SeatGlobal::get_handle(wlSeat);
330 1 Q_EMIT priv->handle->windowMenuRequested(seat, serial, QPoint(pos_x, pos_y));
331 1 }
332
333 1 void XdgShellToplevel::Private::setMaximizedCallback([[maybe_unused]] wl_client* wlClient,
334 wl_resource* wlResource)
335 {
336 1 auto priv = get_handle(wlResource)->d_ptr;
337 1 Q_EMIT priv->handle->maximizedChanged(true);
338 1 }
339
340 1 void XdgShellToplevel::Private::unsetMaximizedCallback([[maybe_unused]] wl_client* wlClient,
341 wl_resource* wlResource)
342 {
343 1 auto priv = get_handle(wlResource)->d_ptr;
344 1 Q_EMIT priv->handle->maximizedChanged(false);
345 1 }
346
347 3 void XdgShellToplevel::Private::setFullscreenCallback([[maybe_unused]] wl_client* wlClient,
348 wl_resource* wlResource,
349 wl_resource* wlOutput)
350 {
351 3 auto priv = get_handle(wlResource)->d_ptr;
352
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 auto output = wlOutput ? WlOutputGlobal::get_handle(wlOutput)->output() : nullptr;
353
354 3 Q_EMIT priv->handle->fullscreenChanged(true, output);
355 3 }
356
357 1 void XdgShellToplevel::Private::unsetFullscreenCallback([[maybe_unused]] wl_client* wlClient,
358 wl_resource* wlResource)
359 {
360 1 auto priv = get_handle(wlResource)->d_ptr;
361 1 Q_EMIT priv->handle->fullscreenChanged(false, nullptr);
362 1 }
363
364 1 void XdgShellToplevel::Private::setMinimizedCallback([[maybe_unused]] wl_client* wlClient,
365 wl_resource* wlResource)
366 {
367 1 auto priv = get_handle(wlResource)->d_ptr;
368 1 Q_EMIT priv->handle->minimizeRequested();
369 1 }
370
371 69 XdgShellToplevel::XdgShellToplevel(uint32_t version, uint32_t id, XdgShellSurface* surface)
372 69 : QObject(nullptr)
373
2/4
✓ Branch 0 taken 69 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 69 times.
69 , d_ptr(new Private(version, id, surface, this))
374 69 {
375 69 }
376
377 7 XdgShellToplevel* XdgShellToplevel::transientFor() const
378 {
379 7 return d_ptr->parentSurface;
380 }
381
382 2 QSize XdgShellToplevel::minimumSize() const
383 {
384 2 return d_ptr->minimumSize();
385 }
386
387 2 QSize XdgShellToplevel::maximumSize() const
388 {
389 2 return d_ptr->maximumSize();
390 }
391
392 3 void XdgShellToplevel::configure_bounds(QSize const& bounds)
393 {
394
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 1 times.
3 auto val = bounds.isValid() ? bounds : QSize(0, 0);
395 6 d_ptr->send<xdg_toplevel_send_configure_bounds, XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION>(
396 3 val.width(), val.height());
397 3 }
398
399 1 void XdgShellToplevel::unconfigure_bounds()
400 {
401 1 configure_bounds({});
402 1 }
403
404 39 uint32_t XdgShellToplevel::configure(XdgShellSurface::States states, QSize const& size)
405 {
406 39 return d_ptr->configure(states, size);
407 }
408
409 1 bool XdgShellToplevel::configurePending() const
410 {
411 1 return d_ptr->shellSurface->configurePending();
412 }
413
414 2 void XdgShellToplevel::set_capabilities(std::set<xdg_shell_wm_capability> const& caps) const
415 {
416 wl_array wlcaps;
417 2 wl_array_init(&wlcaps);
418
419 6 auto get_wlcap = [](auto cap) -> xdg_toplevel_wm_capabilities {
420
3/5
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✓ Branch 4 taken 2 times.
4 switch (cap) {
421 case xdg_shell_wm_capability::window_menu:
422 return XDG_TOPLEVEL_WM_CAPABILITIES_WINDOW_MENU;
423 case xdg_shell_wm_capability::maximize:
424 1 return XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE;
425 case xdg_shell_wm_capability::fullscreen:
426 1 return XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN;
427 case xdg_shell_wm_capability::minimize:
428 2 return XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE;
429 }
430
431 // Should never reach. Return some value so it builds in release config.
432 assert(false);
433 return XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE;
434 4 };
435
436
2/2
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 2 times.
6 for (auto cap : caps) {
437 4 auto state = static_cast<uint32_t*>(wl_array_add(&wlcaps, sizeof(uint32_t)));
438 4 *state = get_wlcap(cap);
439 }
440
441 4 d_ptr->send<xdg_toplevel_send_wm_capabilities, XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION>(
442 2 &wlcaps);
443 2 wl_array_release(&wlcaps);
444 2 }
445
446 1 void XdgShellToplevel::close()
447 {
448 1 d_ptr->close();
449 1 }
450
451 16 XdgShellSurface* XdgShellToplevel::surface() const
452 {
453 16 return d_ptr->shellSurface;
454 }
455
456 2 Client* XdgShellToplevel::client() const
457 {
458 2 return d_ptr->client->handle;
459 }
460
461 3 std::string XdgShellToplevel::title() const
462 {
463 3 return d_ptr->title;
464 }
465
466 3 std::string XdgShellToplevel::appId() const
467 {
468 3 return d_ptr->appId;
469 }
470
471 }
472