1/*
2 SPDX-FileCopyrightText: 2017 David Edmundson <davidedmundson@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6#include "event_queue.h"
7#include "output.h"
8#include "seat.h"
9#include "surface.h"
10#include "wayland_pointer_p.h"
11#include "xdgshell_p.h"
12#include <wayland-xdg-shell-client-protocol.h>
13
14namespace KWayland
15{
16namespace Client
17{
18class XdgShellStable::Private : public XdgShell::Private
19{
20public:
21 void setup(xdg_wm_base *shell) override;
22 void release() override;
23 void destroy() override;
24 bool isValid() const override;
25 XdgShellSurface *getXdgSurface(Surface *surface, QObject *parent) override;
26
27 XdgShellPopup *getXdgPopup(Surface *surface, XdgShellSurface *parentSurface, const XdgPositioner &positioner, QObject *parent) override;
28 XdgShellPopup *getXdgPopup(Surface *surface, XdgShellPopup *parentSurface, const XdgPositioner &positioner, QObject *parent) override;
29
30 using XdgShell::Private::operator xdg_shell *;
31 using XdgShell::Private::operator zxdg_shell_v6 *;
32 operator xdg_wm_base *() override
33 {
34 return xdg_shell_base;
35 }
36 operator xdg_wm_base *() const override
37 {
38 return xdg_shell_base;
39 }
40
41private:
42 XdgShellPopup *internalGetXdgPopup(Surface *surface, xdg_surface *parentSurface, const XdgPositioner &positioner, QObject *parent);
43 static void pingCallback(void *data, struct xdg_wm_base *shell, uint32_t serial);
44
45 WaylandPointer<xdg_wm_base, xdg_wm_base_destroy> xdg_shell_base;
46 static const struct xdg_wm_base_listener s_shellListener;
47};
48
49const struct xdg_wm_base_listener XdgShellStable::Private::s_shellListener = {
50 .ping: pingCallback,
51};
52
53void XdgShellStable::Private::pingCallback(void *data, struct xdg_wm_base *shell, uint32_t serial)
54{
55 Q_UNUSED(data)
56 xdg_wm_base_pong(xdg_wm_base: shell, serial);
57}
58
59void XdgShellStable::Private::setup(xdg_wm_base *shell)
60{
61 Q_ASSERT(shell);
62 Q_ASSERT(!xdg_shell_base);
63 xdg_shell_base.setup(pointer: shell);
64 xdg_wm_base_add_listener(xdg_wm_base: shell, listener: &s_shellListener, data: this);
65}
66
67void XdgShellStable::Private::release()
68{
69 xdg_shell_base.release();
70}
71
72void XdgShellStable::Private::destroy()
73{
74 xdg_shell_base.destroy();
75}
76
77bool XdgShellStable::Private::isValid() const
78{
79 return xdg_shell_base.isValid();
80}
81
82XdgShellSurface *XdgShellStable::Private::getXdgSurface(Surface *surface, QObject *parent)
83{
84 Q_ASSERT(isValid());
85 auto ss = xdg_wm_base_get_xdg_surface(xdg_wm_base: xdg_shell_base, surface: *surface);
86
87 if (!ss) {
88 return nullptr;
89 }
90
91 auto s = new XdgTopLevelStable(parent);
92 auto toplevel = xdg_surface_get_toplevel(xdg_surface: ss);
93 if (queue) {
94 queue->addProxy(proxy: ss);
95 queue->addProxy(proxy: toplevel);
96 }
97 s->setup(xdgsurface: ss, toplevel);
98 return s;
99}
100
101XdgShellPopup *XdgShellStable::Private::getXdgPopup(Surface *surface, XdgShellSurface *parentSurface, const XdgPositioner &positioner, QObject *parent)
102{
103 return internalGetXdgPopup(surface, parentSurface: *parentSurface, positioner, parent);
104}
105
106XdgShellPopup *XdgShellStable::Private::getXdgPopup(Surface *surface, XdgShellPopup *parentSurface, const XdgPositioner &positioner, QObject *parent)
107{
108 return internalGetXdgPopup(surface, parentSurface: *parentSurface, positioner, parent);
109}
110
111XdgShellPopup *XdgShellStable::Private::internalGetXdgPopup(Surface *surface, xdg_surface *parentSurface, const XdgPositioner &positioner, QObject *parent)
112{
113 Q_ASSERT(isValid());
114 auto ss = xdg_wm_base_get_xdg_surface(xdg_wm_base: xdg_shell_base, surface: *surface);
115 if (!ss) {
116 return nullptr;
117 }
118
119 auto p = xdg_wm_base_create_positioner(xdg_wm_base: xdg_shell_base);
120
121 auto anchorRect = positioner.anchorRect();
122 xdg_positioner_set_anchor_rect(xdg_positioner: p, x: anchorRect.x(), y: anchorRect.y(), width: anchorRect.width(), height: anchorRect.height());
123
124 QSize initialSize = positioner.initialSize();
125 xdg_positioner_set_size(xdg_positioner: p, width: initialSize.width(), height: initialSize.height());
126
127 QPoint anchorOffset = positioner.anchorOffset();
128 if (!anchorOffset.isNull()) {
129 xdg_positioner_set_offset(xdg_positioner: p, x: anchorOffset.x(), y: anchorOffset.y());
130 }
131
132 uint32_t anchor = XDG_POSITIONER_ANCHOR_NONE;
133 if (positioner.anchorEdge().testFlag(flag: Qt::TopEdge)) {
134 if (positioner.anchorEdge().testFlag(flag: Qt::LeftEdge) && ((positioner.anchorEdge() & ~Qt::LeftEdge) == Qt::TopEdge)) {
135 anchor = XDG_POSITIONER_ANCHOR_TOP_LEFT;
136 } else if (positioner.anchorEdge().testFlag(flag: Qt::RightEdge) && ((positioner.anchorEdge() & ~Qt::RightEdge) == Qt::TopEdge)) {
137 anchor = XDG_POSITIONER_ANCHOR_TOP_RIGHT;
138 } else if ((positioner.anchorEdge() & ~Qt::TopEdge) == Qt::Edges()) {
139 anchor = XDG_POSITIONER_ANCHOR_TOP;
140 }
141 } else if (positioner.anchorEdge().testFlag(flag: Qt::BottomEdge)) {
142 if (positioner.anchorEdge().testFlag(flag: Qt::LeftEdge) && ((positioner.anchorEdge() & ~Qt::LeftEdge) == Qt::BottomEdge)) {
143 anchor = XDG_POSITIONER_ANCHOR_BOTTOM_LEFT;
144 } else if (positioner.anchorEdge().testFlag(flag: Qt::RightEdge) && ((positioner.anchorEdge() & ~Qt::RightEdge) == Qt::BottomEdge)) {
145 anchor = XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT;
146 } else if ((positioner.anchorEdge() & ~Qt::BottomEdge) == Qt::Edges()) {
147 anchor = XDG_POSITIONER_ANCHOR_BOTTOM;
148 }
149 } else if (positioner.anchorEdge().testFlag(flag: Qt::RightEdge) && ((positioner.anchorEdge() & ~Qt::RightEdge) == Qt::Edges())) {
150 anchor = XDG_POSITIONER_ANCHOR_RIGHT;
151 } else if (positioner.anchorEdge().testFlag(flag: Qt::LeftEdge) && ((positioner.anchorEdge() & ~Qt::LeftEdge) == Qt::Edges())) {
152 anchor = XDG_POSITIONER_ANCHOR_LEFT;
153 }
154 if (anchor != 0) {
155 xdg_positioner_set_anchor(xdg_positioner: p, anchor);
156 }
157
158 uint32_t gravity = XDG_POSITIONER_GRAVITY_NONE;
159 if (positioner.gravity().testFlag(flag: Qt::TopEdge)) {
160 if (positioner.gravity().testFlag(flag: Qt::LeftEdge) && ((positioner.gravity() & ~Qt::LeftEdge) == Qt::TopEdge)) {
161 gravity = XDG_POSITIONER_GRAVITY_TOP_LEFT;
162 } else if (positioner.gravity().testFlag(flag: Qt::RightEdge) && ((positioner.gravity() & ~Qt::RightEdge) == Qt::TopEdge)) {
163 gravity = XDG_POSITIONER_GRAVITY_TOP_RIGHT;
164 } else if ((positioner.gravity() & ~Qt::TopEdge) == Qt::Edges()) {
165 gravity = XDG_POSITIONER_GRAVITY_TOP;
166 }
167 } else if (positioner.gravity().testFlag(flag: Qt::BottomEdge)) {
168 if (positioner.gravity().testFlag(flag: Qt::LeftEdge) && ((positioner.gravity() & ~Qt::LeftEdge) == Qt::BottomEdge)) {
169 gravity = XDG_POSITIONER_GRAVITY_BOTTOM_LEFT;
170 } else if (positioner.gravity().testFlag(flag: Qt::RightEdge) && ((positioner.gravity() & ~Qt::RightEdge) == Qt::BottomEdge)) {
171 gravity = XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT;
172 } else if ((positioner.gravity() & ~Qt::BottomEdge) == Qt::Edges()) {
173 gravity = XDG_POSITIONER_GRAVITY_BOTTOM;
174 }
175 } else if (positioner.gravity().testFlag(flag: Qt::RightEdge) && ((positioner.gravity() & ~Qt::RightEdge) == Qt::Edges())) {
176 gravity = XDG_POSITIONER_GRAVITY_RIGHT;
177 } else if (positioner.gravity().testFlag(flag: Qt::LeftEdge) && ((positioner.gravity() & ~Qt::LeftEdge) == Qt::Edges())) {
178 gravity = XDG_POSITIONER_GRAVITY_LEFT;
179 }
180 if (gravity != 0) {
181 xdg_positioner_set_gravity(xdg_positioner: p, gravity);
182 }
183
184 uint32_t constraint = XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_NONE;
185 if (positioner.constraints().testFlag(flag: XdgPositioner::Constraint::SlideX)) {
186 constraint |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X;
187 }
188 if (positioner.constraints().testFlag(flag: XdgPositioner::Constraint::SlideY)) {
189 constraint |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y;
190 }
191 if (positioner.constraints().testFlag(flag: XdgPositioner::Constraint::FlipX)) {
192 constraint |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X;
193 }
194 if (positioner.constraints().testFlag(flag: XdgPositioner::Constraint::FlipY)) {
195 constraint |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y;
196 }
197 if (positioner.constraints().testFlag(flag: XdgPositioner::Constraint::ResizeX)) {
198 constraint |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X;
199 }
200 if (positioner.constraints().testFlag(flag: XdgPositioner::Constraint::ResizeY)) {
201 constraint |= XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y;
202 }
203 if (constraint != 0) {
204 xdg_positioner_set_constraint_adjustment(xdg_positioner: p, constraint_adjustment: constraint);
205 }
206
207 XdgShellPopup *s = new XdgShellPopupStable(parent);
208 auto popup = xdg_surface_get_popup(xdg_surface: ss, parent: parentSurface, positioner: p);
209 if (queue) {
210 // deliberately not adding the positioner because the positioner has no events sent to it
211 queue->addProxy(proxy: ss);
212 queue->addProxy(proxy: popup);
213 }
214 s->setup(xdgsurface: ss, xdgpopup: popup);
215
216 xdg_positioner_destroy(xdg_positioner: p);
217
218 return s;
219}
220
221XdgShellStable::XdgShellStable(QObject *parent)
222 : XdgShell(new Private, parent)
223{
224}
225
226XdgShellStable::~XdgShellStable() = default;
227
228// A top level wraps both xdg_surface and xdg_top_level into the public API XdgShelllSurface
229class XdgTopLevelStable::Private : public XdgShellSurface::Private
230{
231public:
232 Private(XdgShellSurface *q);
233 WaylandPointer<xdg_toplevel, xdg_toplevel_destroy> xdgtoplevel;
234 WaylandPointer<xdg_surface, xdg_surface_destroy> xdgsurface;
235
236 void setup(xdg_surface *surface, xdg_toplevel *toplevel) override;
237 void release() override;
238 void destroy() override;
239 bool isValid() const override;
240
241 using XdgShellSurface::Private::operator zxdg_toplevel_v6 *;
242 using XdgShellSurface::Private::operator zxdg_surface_v6 *;
243 operator xdg_surface *() override
244 {
245 return xdgsurface;
246 }
247 operator xdg_surface *() const override
248 {
249 return xdgsurface;
250 }
251 operator xdg_toplevel *() override
252 {
253 return xdgtoplevel;
254 }
255 operator xdg_toplevel *() const override
256 {
257 return xdgtoplevel;
258 }
259
260 void setTransientFor(XdgShellSurface *parent) override;
261 void setTitle(const QString &title) override;
262 void setAppId(const QByteArray &appId) override;
263 void showWindowMenu(Seat *seat, quint32 serial, qint32 x, qint32 y) override;
264 void move(Seat *seat, quint32 serial) override;
265 void resize(Seat *seat, quint32 serial, Qt::Edges edges) override;
266 void ackConfigure(quint32 serial) override;
267 void setMaximized() override;
268 void unsetMaximized() override;
269 void setFullscreen(Output *output) override;
270 void unsetFullscreen() override;
271 void setMinimized() override;
272 void setMaxSize(const QSize &size) override;
273 void setMinSize(const QSize &size) override;
274 void setWindowGeometry(const QRect &windowGeometry) override;
275
276private:
277 QSize pendingSize;
278 States pendingState;
279
280 static void configureCallback(void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *state);
281 static void closeCallback(void *data, xdg_toplevel *xdg_toplevel);
282 static void surfaceConfigureCallback(void *data, xdg_surface *xdg_surface, uint32_t serial);
283
284 static const struct xdg_toplevel_listener s_toplevelListener;
285 static const struct xdg_surface_listener s_surfaceListener;
286};
287
288const struct xdg_toplevel_listener XdgTopLevelStable::Private::s_toplevelListener = {.configure: configureCallback, .close: closeCallback};
289
290const struct xdg_surface_listener XdgTopLevelStable::Private::s_surfaceListener = {.configure: surfaceConfigureCallback};
291
292void XdgTopLevelStable::Private::surfaceConfigureCallback(void *data, struct xdg_surface *surface, uint32_t serial)
293{
294 Q_UNUSED(surface)
295 auto s = static_cast<Private *>(data);
296 s->q->configureRequested(size: s->pendingSize, states: s->pendingState, serial);
297 if (!s->pendingSize.isNull()) {
298 s->q->setSize(s->pendingSize);
299 s->pendingSize = QSize();
300 }
301 s->pendingState = {};
302}
303
304void XdgTopLevelStable::Private::configureCallback(void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *state)
305{
306 Q_UNUSED(xdg_toplevel)
307 auto s = static_cast<Private *>(data);
308 States states;
309
310 uint32_t *statePtr = static_cast<uint32_t *>(state->data);
311 for (size_t i = 0; i < state->size / sizeof(uint32_t); i++) {
312 switch (statePtr[i]) {
313 case XDG_TOPLEVEL_STATE_MAXIMIZED:
314 states = states | XdgShellSurface::State::Maximized;
315 break;
316 case XDG_TOPLEVEL_STATE_FULLSCREEN:
317 states = states | XdgShellSurface::State::Fullscreen;
318 break;
319 case XDG_TOPLEVEL_STATE_RESIZING:
320 states = states | XdgShellSurface::State::Resizing;
321 break;
322 case XDG_TOPLEVEL_STATE_ACTIVATED:
323 states = states | XdgShellSurface::State::Activated;
324 break;
325 }
326 }
327 s->pendingSize = QSize(width, height);
328 s->pendingState = states;
329}
330
331void XdgTopLevelStable::Private::closeCallback(void *data, xdg_toplevel *xdg_toplevel)
332{
333 auto s = static_cast<XdgTopLevelStable::Private *>(data);
334 Q_ASSERT(s->xdgtoplevel == xdg_toplevel);
335 Q_EMIT s->q->closeRequested();
336}
337
338XdgTopLevelStable::Private::Private(XdgShellSurface *q)
339 : XdgShellSurface::Private(q)
340{
341}
342
343void XdgTopLevelStable::Private::setup(xdg_surface *surface, xdg_toplevel *topLevel)
344{
345 Q_ASSERT(surface);
346 Q_ASSERT(!xdgtoplevel);
347 xdgsurface.setup(pointer: surface);
348 xdgtoplevel.setup(pointer: topLevel);
349 xdg_surface_add_listener(xdg_surface: xdgsurface, listener: &s_surfaceListener, data: this);
350 xdg_toplevel_add_listener(xdg_toplevel: xdgtoplevel, listener: &s_toplevelListener, data: this);
351}
352
353void XdgTopLevelStable::Private::release()
354{
355 xdgtoplevel.release();
356 xdgsurface.release();
357}
358
359void XdgTopLevelStable::Private::destroy()
360{
361 xdgtoplevel.destroy();
362 xdgsurface.destroy();
363}
364
365bool XdgTopLevelStable::Private::isValid() const
366{
367 return xdgtoplevel.isValid() && xdgsurface.isValid();
368}
369
370void XdgTopLevelStable::Private::setTransientFor(XdgShellSurface *parent)
371{
372 xdg_toplevel *parentSurface = nullptr;
373 if (parent) {
374 parentSurface = *parent;
375 }
376 xdg_toplevel_set_parent(xdg_toplevel: xdgtoplevel, parent: parentSurface);
377}
378
379void XdgTopLevelStable::Private::setTitle(const QString &title)
380{
381 xdg_toplevel_set_title(xdg_toplevel: xdgtoplevel, title: title.toUtf8().constData());
382}
383
384void XdgTopLevelStable::Private::setAppId(const QByteArray &appId)
385{
386 xdg_toplevel_set_app_id(xdg_toplevel: xdgtoplevel, app_id: appId.constData());
387}
388
389void XdgTopLevelStable::Private::showWindowMenu(Seat *seat, quint32 serial, qint32 x, qint32 y)
390{
391 xdg_toplevel_show_window_menu(xdg_toplevel: xdgtoplevel, seat: *seat, serial, x, y);
392}
393
394void XdgTopLevelStable::Private::move(Seat *seat, quint32 serial)
395{
396 xdg_toplevel_move(xdg_toplevel: xdgtoplevel, seat: *seat, serial);
397}
398
399void XdgTopLevelStable::Private::resize(Seat *seat, quint32 serial, Qt::Edges edges)
400{
401 uint wlEdge = XDG_TOPLEVEL_RESIZE_EDGE_NONE;
402 if (edges.testFlag(flag: Qt::TopEdge)) {
403 if (edges.testFlag(flag: Qt::LeftEdge) && ((edges & ~Qt::LeftEdge) == Qt::TopEdge)) {
404 wlEdge = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT;
405 } else if (edges.testFlag(flag: Qt::RightEdge) && ((edges & ~Qt::RightEdge) == Qt::TopEdge)) {
406 wlEdge = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT;
407 } else if ((edges & ~Qt::TopEdge) == Qt::Edges()) {
408 wlEdge = XDG_TOPLEVEL_RESIZE_EDGE_TOP;
409 }
410 } else if (edges.testFlag(flag: Qt::BottomEdge)) {
411 if (edges.testFlag(flag: Qt::LeftEdge) && ((edges & ~Qt::LeftEdge) == Qt::BottomEdge)) {
412 wlEdge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT;
413 } else if (edges.testFlag(flag: Qt::RightEdge) && ((edges & ~Qt::RightEdge) == Qt::BottomEdge)) {
414 wlEdge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT;
415 } else if ((edges & ~Qt::BottomEdge) == Qt::Edges()) {
416 wlEdge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM;
417 }
418 } else if (edges.testFlag(flag: Qt::RightEdge) && ((edges & ~Qt::RightEdge) == Qt::Edges())) {
419 wlEdge = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT;
420 } else if (edges.testFlag(flag: Qt::LeftEdge) && ((edges & ~Qt::LeftEdge) == Qt::Edges())) {
421 wlEdge = XDG_TOPLEVEL_RESIZE_EDGE_LEFT;
422 }
423 xdg_toplevel_resize(xdg_toplevel: xdgtoplevel, seat: *seat, serial, edges: wlEdge);
424}
425
426void XdgTopLevelStable::Private::ackConfigure(quint32 serial)
427{
428 xdg_surface_ack_configure(xdg_surface: xdgsurface, serial);
429}
430
431void XdgTopLevelStable::Private::setMaximized()
432{
433 xdg_toplevel_set_maximized(xdg_toplevel: xdgtoplevel);
434}
435
436void XdgTopLevelStable::Private::unsetMaximized()
437{
438 xdg_toplevel_unset_maximized(xdg_toplevel: xdgtoplevel);
439}
440
441void XdgTopLevelStable::Private::setFullscreen(Output *output)
442{
443 wl_output *o = nullptr;
444 if (output) {
445 o = *output;
446 }
447 xdg_toplevel_set_fullscreen(xdg_toplevel: xdgtoplevel, output: o);
448}
449
450void XdgTopLevelStable::Private::unsetFullscreen()
451{
452 xdg_toplevel_unset_fullscreen(xdg_toplevel: xdgtoplevel);
453}
454
455void XdgTopLevelStable::Private::setMinimized()
456{
457 xdg_toplevel_set_minimized(xdg_toplevel: xdgtoplevel);
458}
459
460void XdgTopLevelStable::Private::setMaxSize(const QSize &size)
461{
462 xdg_toplevel_set_max_size(xdg_toplevel: xdgtoplevel, width: size.width(), height: size.height());
463}
464
465void XdgTopLevelStable::Private::setMinSize(const QSize &size)
466{
467 xdg_toplevel_set_min_size(xdg_toplevel: xdgtoplevel, width: size.width(), height: size.height());
468}
469
470void XdgTopLevelStable::Private::setWindowGeometry(const QRect &windowGeometry)
471{
472 xdg_surface_set_window_geometry(xdg_surface: xdgsurface, x: windowGeometry.x(), y: windowGeometry.y(), width: windowGeometry.width(), height: windowGeometry.height());
473}
474
475XdgTopLevelStable::XdgTopLevelStable(QObject *parent)
476 : XdgShellSurface(new Private(this), parent)
477{
478}
479
480XdgTopLevelStable::~XdgTopLevelStable() = default;
481
482class XdgShellPopupStable::Private : public XdgShellPopup::Private
483{
484public:
485 Private(XdgShellPopup *q);
486
487 void setup(xdg_surface *s, xdg_popup *p) override;
488 void release() override;
489 void destroy() override;
490 bool isValid() const override;
491 void requestGrab(Seat *seat, quint32 serial) override;
492 void ackConfigure(quint32 serial) override;
493 void setWindowGeometry(const QRect &windowGeometry) override;
494
495 using XdgShellPopup::Private::operator zxdg_popup_v6 *;
496 using XdgShellPopup::Private::operator zxdg_surface_v6 *;
497 operator xdg_surface *() override
498 {
499 return xdgsurface;
500 }
501 operator xdg_surface *() const override
502 {
503 return xdgsurface;
504 }
505 operator xdg_popup *() override
506 {
507 return xdgpopup;
508 }
509 operator xdg_popup *() const override
510 {
511 return xdgpopup;
512 }
513 WaylandPointer<xdg_surface, xdg_surface_destroy> xdgsurface;
514 WaylandPointer<xdg_popup, xdg_popup_destroy> xdgpopup;
515
516 QRect pendingRect;
517
518private:
519 static void configureCallback(void *data, xdg_popup *xdg_popup, int32_t x, int32_t y, int32_t width, int32_t height);
520 static void popupDoneCallback(void *data, xdg_popup *xdg_popup);
521 static void surfaceConfigureCallback(void *data, xdg_surface *xdg_surface, uint32_t serial);
522
523 static const struct xdg_popup_listener s_popupListener;
524 static const struct xdg_surface_listener s_surfaceListener;
525};
526
527const struct xdg_popup_listener XdgShellPopupStable::Private::s_popupListener = {.configure: configureCallback, .popup_done: popupDoneCallback};
528
529const struct xdg_surface_listener XdgShellPopupStable::Private::s_surfaceListener = {
530 .configure: surfaceConfigureCallback,
531};
532
533void XdgShellPopupStable::Private::configureCallback(void *data, xdg_popup *xdg_popup, int32_t x, int32_t y, int32_t width, int32_t height)
534{
535 Q_UNUSED(xdg_popup)
536 auto s = static_cast<Private *>(data);
537 s->pendingRect = QRect(x, y, width, height);
538}
539
540void XdgShellPopupStable::Private::surfaceConfigureCallback(void *data, struct xdg_surface *surface, uint32_t serial)
541{
542 Q_UNUSED(surface)
543 auto s = static_cast<Private *>(data);
544 s->q->configureRequested(relativePosition: s->pendingRect, serial);
545 s->pendingRect = QRect();
546}
547
548void XdgShellPopupStable::Private::popupDoneCallback(void *data, xdg_popup *xdg_popup)
549{
550 auto s = static_cast<XdgShellPopupStable::Private *>(data);
551 Q_ASSERT(s->xdgpopup == xdg_popup);
552 Q_EMIT s->q->popupDone();
553}
554
555XdgShellPopupStable::Private::Private(XdgShellPopup *q)
556 : XdgShellPopup::Private(q)
557{
558}
559
560void XdgShellPopupStable::Private::setup(xdg_surface *s, xdg_popup *p)
561{
562 Q_ASSERT(p);
563 Q_ASSERT(!xdgsurface);
564 Q_ASSERT(!xdgpopup);
565
566 xdgsurface.setup(pointer: s);
567 xdgpopup.setup(pointer: p);
568 xdg_surface_add_listener(xdg_surface: xdgsurface, listener: &s_surfaceListener, data: this);
569 xdg_popup_add_listener(xdg_popup: xdgpopup, listener: &s_popupListener, data: this);
570}
571
572void XdgShellPopupStable::Private::release()
573{
574 xdgpopup.release();
575}
576
577void XdgShellPopupStable::Private::destroy()
578{
579 xdgpopup.destroy();
580}
581
582bool XdgShellPopupStable::Private::isValid() const
583{
584 return xdgpopup.isValid();
585}
586
587void XdgShellPopupStable::Private::requestGrab(Seat *seat, quint32 serial)
588{
589 xdg_popup_grab(xdg_popup: xdgpopup, seat: *seat, serial);
590}
591
592void XdgShellPopupStable::Private::ackConfigure(quint32 serial)
593{
594 xdg_surface_ack_configure(xdg_surface: xdgsurface, serial);
595}
596
597void XdgShellPopupStable::Private::setWindowGeometry(const QRect &windowGeometry)
598{
599 xdg_surface_set_window_geometry(xdg_surface: xdgsurface, x: windowGeometry.x(), y: windowGeometry.y(), width: windowGeometry.width(), height: windowGeometry.height());
600}
601
602XdgShellPopupStable::XdgShellPopupStable(QObject *parent)
603 : XdgShellPopup(new Private(this), parent)
604{
605}
606
607XdgShellPopupStable::~XdgShellPopupStable() = default;
608
609}
610}
611

source code of kwayland/src/client/xdgshell_stable.cpp