| 1 | /* |
| 2 | SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org> |
| 3 | |
| 4 | SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL |
| 5 | */ |
| 6 | #include "../compat/wayland-xdg-shell-v5-client-protocol.h" |
| 7 | #include "event_queue.h" |
| 8 | #include "output.h" |
| 9 | #include "seat.h" |
| 10 | #include "surface.h" |
| 11 | #include "wayland_pointer_p.h" |
| 12 | #include "xdgshell_p.h" |
| 13 | |
| 14 | namespace KWayland |
| 15 | { |
| 16 | namespace Client |
| 17 | { |
| 18 | class XdgShellUnstableV5::Private : public XdgShell::Private |
| 19 | { |
| 20 | public: |
| 21 | void setupV5(xdg_shell *shell) override; |
| 22 | void release() override; |
| 23 | void destroy() override; |
| 24 | bool isValid() const override; |
| 25 | XdgShellSurface *getXdgSurface(Surface *surface, QObject *parent) override; |
| 26 | XdgShellPopup *getXdgPopup(Surface *surface, Surface *parentSurface, Seat *seat, quint32 serial, const QPoint &parentPos, QObject *parent) override; |
| 27 | |
| 28 | using XdgShell::Private::operator xdg_wm_base *; |
| 29 | using XdgShell::Private::operator zxdg_shell_v6 *; |
| 30 | operator xdg_shell *() override |
| 31 | { |
| 32 | return xdgshellv5; |
| 33 | } |
| 34 | operator xdg_shell *() const override |
| 35 | { |
| 36 | return xdgshellv5; |
| 37 | } |
| 38 | |
| 39 | static void pingCallback(void *data, struct xdg_shell *shell, uint32_t serial); |
| 40 | |
| 41 | WaylandPointer<xdg_shell, zxdg_shell_v5_destroy> xdgshellv5; |
| 42 | static const struct zxdg_shell_v5_listener s_shellListener; |
| 43 | }; |
| 44 | |
| 45 | const struct zxdg_shell_v5_listener XdgShellUnstableV5::Private::s_shellListener = { |
| 46 | .ping: pingCallback, |
| 47 | }; |
| 48 | |
| 49 | void XdgShellUnstableV5::Private::pingCallback(void *data, struct xdg_shell *shell, uint32_t serial) |
| 50 | { |
| 51 | Q_UNUSED(data); |
| 52 | zxdg_shell_v5_pong(xdg_shell: shell, serial); |
| 53 | } |
| 54 | |
| 55 | void XdgShellUnstableV5::Private::setupV5(xdg_shell *shell) |
| 56 | { |
| 57 | Q_ASSERT(shell); |
| 58 | Q_ASSERT(!xdgshellv5); |
| 59 | xdgshellv5.setup(pointer: shell); |
| 60 | zxdg_shell_v5_use_unstable_version(xdg_shell: xdgshellv5, version: 5); |
| 61 | zxdg_shell_v5_add_listener(xdg_shell: shell, listener: &s_shellListener, data: this); |
| 62 | } |
| 63 | |
| 64 | void XdgShellUnstableV5::Private::release() |
| 65 | { |
| 66 | xdgshellv5.release(); |
| 67 | } |
| 68 | |
| 69 | void XdgShellUnstableV5::Private::destroy() |
| 70 | { |
| 71 | xdgshellv5.destroy(); |
| 72 | } |
| 73 | |
| 74 | bool XdgShellUnstableV5::Private::isValid() const |
| 75 | { |
| 76 | return xdgshellv5.isValid(); |
| 77 | } |
| 78 | |
| 79 | XdgShellSurface *XdgShellUnstableV5::Private::getXdgSurface(Surface *surface, QObject *parent) |
| 80 | { |
| 81 | Q_ASSERT(isValid()); |
| 82 | XdgShellSurface *s = new XdgShellSurfaceUnstableV5(parent); |
| 83 | auto w = zxdg_shell_v5_get_xdg_surface(xdg_shell: xdgshellv5, surface: *surface); |
| 84 | if (queue) { |
| 85 | queue->addProxy(proxy: w); |
| 86 | } |
| 87 | s->setup(w); |
| 88 | return s; |
| 89 | } |
| 90 | |
| 91 | XdgShellPopup * |
| 92 | XdgShellUnstableV5::Private::getXdgPopup(Surface *surface, Surface *parentSurface, Seat *seat, quint32 serial, const QPoint &parentPos, QObject *parent) |
| 93 | { |
| 94 | Q_ASSERT(isValid()); |
| 95 | XdgShellPopup *s = new XdgShellPopupUnstableV5(parent); |
| 96 | auto w = zxdg_shell_v5_get_xdg_popup(xdg_shell: xdgshellv5, surface: *surface, parent: *parentSurface, seat: *seat, serial, x: parentPos.x(), y: parentPos.y()); |
| 97 | if (queue) { |
| 98 | queue->addProxy(proxy: w); |
| 99 | } |
| 100 | s->setup(w); |
| 101 | return s; |
| 102 | } |
| 103 | |
| 104 | XdgShellUnstableV5::XdgShellUnstableV5(QObject *parent) |
| 105 | : XdgShell(new Private, parent) |
| 106 | { |
| 107 | } |
| 108 | |
| 109 | XdgShellUnstableV5::~XdgShellUnstableV5() = default; |
| 110 | |
| 111 | class XdgShellSurfaceUnstableV5::Private : public XdgShellSurface::Private |
| 112 | { |
| 113 | public: |
| 114 | Private(XdgShellSurface *q); |
| 115 | WaylandPointer<xdg_surface, zxdg_surface_v5_destroy> xdgsurfacev5; |
| 116 | |
| 117 | void setupV5(xdg_surface *surface) override; |
| 118 | void release() override; |
| 119 | void destroy() override; |
| 120 | bool isValid() const override; |
| 121 | |
| 122 | using XdgShellSurface::Private::operator zxdg_surface_v6 *; |
| 123 | using XdgShellSurface::Private::operator zxdg_toplevel_v6 *; |
| 124 | using XdgShellSurface::Private::operator xdg_toplevel *; |
| 125 | operator xdg_surface *() override |
| 126 | { |
| 127 | return xdgsurfacev5; |
| 128 | } |
| 129 | operator xdg_surface *() const override |
| 130 | { |
| 131 | return xdgsurfacev5; |
| 132 | } |
| 133 | |
| 134 | void setTransientFor(XdgShellSurface *parent) override; |
| 135 | void setTitle(const QString &title) override; |
| 136 | void setAppId(const QByteArray &appId) override; |
| 137 | void showWindowMenu(Seat *seat, quint32 serial, qint32 x, qint32 y) override; |
| 138 | void move(Seat *seat, quint32 serial) override; |
| 139 | void resize(Seat *seat, quint32 serial, Qt::Edges edges) override; |
| 140 | void ackConfigure(quint32 serial) override; |
| 141 | void setMaximized() override; |
| 142 | void unsetMaximized() override; |
| 143 | void setFullscreen(Output *output) override; |
| 144 | void unsetFullscreen() override; |
| 145 | void setMinimized() override; |
| 146 | void setMaxSize(const QSize &size) override; |
| 147 | void setMinSize(const QSize &size) override; |
| 148 | |
| 149 | private: |
| 150 | static void configureCallback(void *data, xdg_surface *xdg_surface, int32_t width, int32_t height, wl_array *states, uint32_t serial); |
| 151 | static void closeCallback(void *data, xdg_surface *xdg_surface); |
| 152 | |
| 153 | static const struct zxdg_surface_v5_listener s_listener; |
| 154 | }; |
| 155 | |
| 156 | const struct zxdg_surface_v5_listener XdgShellSurfaceUnstableV5::Private::s_listener = {.configure: configureCallback, .close: closeCallback}; |
| 157 | |
| 158 | void XdgShellSurfaceUnstableV5::Private::configureCallback(void *data, |
| 159 | xdg_surface *xdg_surface, |
| 160 | int32_t width, |
| 161 | int32_t height, |
| 162 | wl_array *wlStates, |
| 163 | uint32_t serial) |
| 164 | { |
| 165 | auto s = reinterpret_cast<XdgShellSurfaceUnstableV5::Private *>(data); |
| 166 | Q_ASSERT(s->xdgsurfacev5 == xdg_surface); |
| 167 | uint32_t *state = reinterpret_cast<uint32_t *>(wlStates->data); |
| 168 | size_t numStates = wlStates->size / sizeof(uint32_t); |
| 169 | States states; |
| 170 | for (size_t i = 0; i < numStates; i++) { |
| 171 | switch (state[i]) { |
| 172 | case ZXDG_SURFACE_V5_STATE_MAXIMIZED: |
| 173 | states = states | XdgShellSurface::State::Maximized; |
| 174 | break; |
| 175 | case ZXDG_SURFACE_V5_STATE_FULLSCREEN: |
| 176 | states = states | XdgShellSurface::State::Fullscreen; |
| 177 | break; |
| 178 | case ZXDG_SURFACE_V5_STATE_RESIZING: |
| 179 | states = states | XdgShellSurface::State::Resizing; |
| 180 | break; |
| 181 | case ZXDG_SURFACE_V5_STATE_ACTIVATED: |
| 182 | states = states | XdgShellSurface::State::Activated; |
| 183 | break; |
| 184 | } |
| 185 | } |
| 186 | const QSize size = QSize(width, height); |
| 187 | Q_EMIT s->q->configureRequested(size, states, serial); |
| 188 | if (!size.isNull()) { |
| 189 | s->q->setSize(size); |
| 190 | } |
| 191 | } |
| 192 | |
| 193 | void XdgShellSurfaceUnstableV5::Private::closeCallback(void *data, xdg_surface *xdg_surface) |
| 194 | { |
| 195 | auto s = reinterpret_cast<XdgShellSurfaceUnstableV5::Private *>(data); |
| 196 | Q_ASSERT(s->xdgsurfacev5 == xdg_surface); |
| 197 | Q_EMIT s->q->closeRequested(); |
| 198 | } |
| 199 | |
| 200 | XdgShellSurfaceUnstableV5::Private::Private(XdgShellSurface *q) |
| 201 | : XdgShellSurface::Private(q) |
| 202 | { |
| 203 | } |
| 204 | |
| 205 | void XdgShellSurfaceUnstableV5::Private::setupV5(xdg_surface *surface) |
| 206 | { |
| 207 | Q_ASSERT(surface); |
| 208 | Q_ASSERT(!xdgsurfacev5); |
| 209 | xdgsurfacev5.setup(pointer: surface); |
| 210 | zxdg_surface_v5_add_listener(xdg_surface: xdgsurfacev5, listener: &s_listener, data: this); |
| 211 | } |
| 212 | |
| 213 | void XdgShellSurfaceUnstableV5::Private::release() |
| 214 | { |
| 215 | xdgsurfacev5.release(); |
| 216 | } |
| 217 | |
| 218 | void XdgShellSurfaceUnstableV5::Private::destroy() |
| 219 | { |
| 220 | xdgsurfacev5.destroy(); |
| 221 | } |
| 222 | |
| 223 | bool XdgShellSurfaceUnstableV5::Private::isValid() const |
| 224 | { |
| 225 | return xdgsurfacev5.isValid(); |
| 226 | } |
| 227 | |
| 228 | void XdgShellSurfaceUnstableV5::Private::setTransientFor(XdgShellSurface *parent) |
| 229 | { |
| 230 | xdg_surface *parentSurface = nullptr; |
| 231 | if (parent) { |
| 232 | parentSurface = *parent; |
| 233 | } |
| 234 | zxdg_surface_v5_set_parent(xdg_surface: xdgsurfacev5, parent: parentSurface); |
| 235 | } |
| 236 | |
| 237 | void XdgShellSurfaceUnstableV5::Private::setTitle(const QString &title) |
| 238 | { |
| 239 | zxdg_surface_v5_set_title(xdg_surface: xdgsurfacev5, title: title.toUtf8().constData()); |
| 240 | } |
| 241 | |
| 242 | void XdgShellSurfaceUnstableV5::Private::setAppId(const QByteArray &appId) |
| 243 | { |
| 244 | zxdg_surface_v5_set_app_id(xdg_surface: xdgsurfacev5, app_id: appId.constData()); |
| 245 | } |
| 246 | |
| 247 | void XdgShellSurfaceUnstableV5::Private::showWindowMenu(Seat *seat, quint32 serial, qint32 x, qint32 y) |
| 248 | { |
| 249 | zxdg_surface_v5_show_window_menu(xdg_surface: xdgsurfacev5, seat: *seat, serial, x, y); |
| 250 | } |
| 251 | |
| 252 | void XdgShellSurfaceUnstableV5::Private::move(Seat *seat, quint32 serial) |
| 253 | { |
| 254 | zxdg_surface_v5_move(xdg_surface: xdgsurfacev5, seat: *seat, serial); |
| 255 | } |
| 256 | |
| 257 | void XdgShellSurfaceUnstableV5::Private::resize(Seat *seat, quint32 serial, Qt::Edges edges) |
| 258 | { |
| 259 | uint wlEdge = ZXDG_SURFACE_V5_RESIZE_EDGE_NONE; |
| 260 | if (edges.testFlag(flag: Qt::TopEdge)) { |
| 261 | if (edges.testFlag(flag: Qt::LeftEdge) && ((edges & ~Qt::LeftEdge) == Qt::TopEdge)) { |
| 262 | wlEdge = ZXDG_SURFACE_V5_RESIZE_EDGE_TOP_LEFT; |
| 263 | } else if (edges.testFlag(flag: Qt::RightEdge) && ((edges & ~Qt::RightEdge) == Qt::TopEdge)) { |
| 264 | wlEdge = ZXDG_SURFACE_V5_RESIZE_EDGE_TOP_RIGHT; |
| 265 | } else if ((edges & ~Qt::TopEdge) == Qt::Edges()) { |
| 266 | wlEdge = ZXDG_SURFACE_V5_RESIZE_EDGE_TOP; |
| 267 | } |
| 268 | } else if (edges.testFlag(flag: Qt::BottomEdge)) { |
| 269 | if (edges.testFlag(flag: Qt::LeftEdge) && ((edges & ~Qt::LeftEdge) == Qt::BottomEdge)) { |
| 270 | wlEdge = ZXDG_SURFACE_V5_RESIZE_EDGE_BOTTOM_LEFT; |
| 271 | } else if (edges.testFlag(flag: Qt::RightEdge) && ((edges & ~Qt::RightEdge) == Qt::BottomEdge)) { |
| 272 | wlEdge = ZXDG_SURFACE_V5_RESIZE_EDGE_BOTTOM_RIGHT; |
| 273 | } else if ((edges & ~Qt::BottomEdge) == Qt::Edges()) { |
| 274 | wlEdge = ZXDG_SURFACE_V5_RESIZE_EDGE_BOTTOM; |
| 275 | } |
| 276 | } else if (edges.testFlag(flag: Qt::RightEdge) && ((edges & ~Qt::RightEdge) == Qt::Edges())) { |
| 277 | wlEdge = ZXDG_SURFACE_V5_RESIZE_EDGE_RIGHT; |
| 278 | } else if (edges.testFlag(flag: Qt::LeftEdge) && ((edges & ~Qt::LeftEdge) == Qt::Edges())) { |
| 279 | wlEdge = ZXDG_SURFACE_V5_RESIZE_EDGE_LEFT; |
| 280 | } |
| 281 | zxdg_surface_v5_resize(xdg_surface: xdgsurfacev5, seat: *seat, serial, edges: wlEdge); |
| 282 | } |
| 283 | |
| 284 | void XdgShellSurfaceUnstableV5::Private::ackConfigure(quint32 serial) |
| 285 | { |
| 286 | zxdg_surface_v5_ack_configure(xdg_surface: xdgsurfacev5, serial); |
| 287 | } |
| 288 | |
| 289 | void XdgShellSurfaceUnstableV5::Private::setMaximized() |
| 290 | { |
| 291 | zxdg_surface_v5_set_maximized(xdg_surface: xdgsurfacev5); |
| 292 | } |
| 293 | |
| 294 | void XdgShellSurfaceUnstableV5::Private::unsetMaximized() |
| 295 | { |
| 296 | zxdg_surface_v5_unset_maximized(xdg_surface: xdgsurfacev5); |
| 297 | } |
| 298 | |
| 299 | void XdgShellSurfaceUnstableV5::Private::setFullscreen(Output *output) |
| 300 | { |
| 301 | wl_output *o = nullptr; |
| 302 | if (output) { |
| 303 | o = *output; |
| 304 | } |
| 305 | zxdg_surface_v5_set_fullscreen(xdg_surface: xdgsurfacev5, output: o); |
| 306 | } |
| 307 | |
| 308 | void XdgShellSurfaceUnstableV5::Private::unsetFullscreen() |
| 309 | { |
| 310 | zxdg_surface_v5_unset_fullscreen(xdg_surface: xdgsurfacev5); |
| 311 | } |
| 312 | |
| 313 | void XdgShellSurfaceUnstableV5::Private::setMinimized() |
| 314 | { |
| 315 | zxdg_surface_v5_set_minimized(xdg_surface: xdgsurfacev5); |
| 316 | } |
| 317 | |
| 318 | void XdgShellSurfaceUnstableV5::Private::setMaxSize(const QSize &size) |
| 319 | { |
| 320 | Q_UNUSED(size) |
| 321 | // TODO: notify an error? |
| 322 | } |
| 323 | |
| 324 | void XdgShellSurfaceUnstableV5::Private::setMinSize(const QSize &size) |
| 325 | { |
| 326 | Q_UNUSED(size) |
| 327 | // TODO: notify an error? |
| 328 | } |
| 329 | |
| 330 | XdgShellSurfaceUnstableV5::XdgShellSurfaceUnstableV5(QObject *parent) |
| 331 | : XdgShellSurface(new Private(this), parent) |
| 332 | { |
| 333 | } |
| 334 | |
| 335 | XdgShellSurfaceUnstableV5::~XdgShellSurfaceUnstableV5() = default; |
| 336 | |
| 337 | class XdgShellPopupUnstableV5::Private : public XdgShellPopup::Private |
| 338 | { |
| 339 | public: |
| 340 | Private(XdgShellPopup *q); |
| 341 | |
| 342 | void setupV5(xdg_popup *p) override; |
| 343 | void release() override; |
| 344 | void destroy() override; |
| 345 | bool isValid() const override; |
| 346 | |
| 347 | using XdgShellPopup::Private::operator xdg_surface *; |
| 348 | using XdgShellPopup::Private::operator zxdg_popup_v6 *; |
| 349 | using XdgShellPopup::Private::operator zxdg_surface_v6 *; |
| 350 | operator xdg_popup *() override |
| 351 | { |
| 352 | return xdgpopupv5; |
| 353 | } |
| 354 | operator xdg_popup *() const override |
| 355 | { |
| 356 | return xdgpopupv5; |
| 357 | } |
| 358 | WaylandPointer<xdg_popup, zxdg_popup_v5_destroy> xdgpopupv5; |
| 359 | |
| 360 | private: |
| 361 | static void popupDoneCallback(void *data, xdg_popup *); |
| 362 | static const struct zxdg_popup_v5_listener s_listener; |
| 363 | }; |
| 364 | |
| 365 | const struct zxdg_popup_v5_listener XdgShellPopupUnstableV5::Private::s_listener = {.popup_done: popupDoneCallback}; |
| 366 | |
| 367 | void XdgShellPopupUnstableV5::Private::popupDoneCallback(void *data, xdg_popup *) |
| 368 | { |
| 369 | auto s = reinterpret_cast<XdgShellPopupUnstableV5::Private *>(data); |
| 370 | Q_ASSERT(s->xdgpopupv5 == xdg_popup); |
| 371 | Q_EMIT s->q->popupDone(); |
| 372 | } |
| 373 | |
| 374 | XdgShellPopupUnstableV5::Private::Private(XdgShellPopup *q) |
| 375 | : XdgShellPopup::Private(q) |
| 376 | { |
| 377 | } |
| 378 | |
| 379 | void XdgShellPopupUnstableV5::Private::setupV5(xdg_popup *p) |
| 380 | { |
| 381 | Q_ASSERT(p); |
| 382 | Q_ASSERT(!xdgpopupv5); |
| 383 | xdgpopupv5.setup(pointer: p); |
| 384 | zxdg_popup_v5_add_listener(xdg_popup: xdgpopupv5, listener: &s_listener, data: this); |
| 385 | } |
| 386 | |
| 387 | void XdgShellPopupUnstableV5::Private::release() |
| 388 | { |
| 389 | xdgpopupv5.release(); |
| 390 | } |
| 391 | |
| 392 | void XdgShellPopupUnstableV5::Private::destroy() |
| 393 | { |
| 394 | xdgpopupv5.destroy(); |
| 395 | } |
| 396 | |
| 397 | bool XdgShellPopupUnstableV5::Private::isValid() const |
| 398 | { |
| 399 | return xdgpopupv5.isValid(); |
| 400 | } |
| 401 | |
| 402 | XdgShellPopupUnstableV5::XdgShellPopupUnstableV5(QObject *parent) |
| 403 | : XdgShellPopup(new Private(this), parent) |
| 404 | { |
| 405 | } |
| 406 | |
| 407 | XdgShellPopupUnstableV5::~XdgShellPopupUnstableV5() = default; |
| 408 | |
| 409 | } |
| 410 | } |
| 411 | |