| 1 | // Copyright (C) 2021 The Qt Company Ltd. |
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
| 3 | |
| 4 | #include "qwaylandqtshell.h" |
| 5 | #include "qwaylandqtshell_p.h" |
| 6 | #include "qwaylandqtshellchrome.h" |
| 7 | |
| 8 | #include <QtWaylandCompositor/QWaylandCompositor> |
| 9 | #include <QtWaylandCompositor/QWaylandSurface> |
| 10 | #include "qwaylandqtshell.h" |
| 11 | #include <QtWaylandCompositor/QWaylandResource> |
| 12 | |
| 13 | #if QT_CONFIG(wayland_compositor_quick) |
| 14 | # include "qwaylandqtshellintegration_p.h" |
| 15 | #endif |
| 16 | |
| 17 | #include <QtWaylandCompositor/QWaylandResource> |
| 18 | #include <QDebug> |
| 19 | #include "compositor_api/qwaylandseat.h" |
| 20 | |
| 21 | #include <QtWaylandCompositor/private/qwaylandutils_p.h> |
| 22 | |
| 23 | QT_BEGIN_NAMESPACE |
| 24 | |
| 25 | /*! |
| 26 | * \qmltype QtShell |
| 27 | * \nativetype QWaylandQtShell |
| 28 | * \inqmlmodule QtWayland.Compositor.QtShell |
| 29 | * \since 6.3 |
| 30 | * \brief Provides a shell extension for Qt applications running on a Qt Wayland Compositor. |
| 31 | * |
| 32 | * The QtShell extension provides a way to associate an QtShellSurface with a regular Wayland |
| 33 | * surface. The QtShell extension is written to support the window management features which are |
| 34 | * supported by Qt. It may be suitable on a platform where both the compositor and client |
| 35 | * applications are written with Qt, and where applications are trusted not to abuse features such |
| 36 | * as manual window positioning and "bring-to-front". |
| 37 | * |
| 38 | * For other use cases, consider using IviApplication or XdgShell instead. |
| 39 | * |
| 40 | * \qml |
| 41 | * import QtWayland.Compositor.QtShell |
| 42 | * |
| 43 | * WaylandCompositor { |
| 44 | * property ListModel shellSurfaces: ListModel {} |
| 45 | * QtShell { |
| 46 | * onQtShellSurfaceCreated: { |
| 47 | * shellSurfaces.append({shellSurface: qtShellSurface}) |
| 48 | * } |
| 49 | * } |
| 50 | * } |
| 51 | * \endqml |
| 52 | */ |
| 53 | QWaylandQtShell::QWaylandQtShell() |
| 54 | : QWaylandCompositorExtensionTemplate<QWaylandQtShell>(*new QWaylandQtShellPrivate()) |
| 55 | { |
| 56 | } |
| 57 | |
| 58 | QWaylandQtShell::QWaylandQtShell(QWaylandCompositor *compositor) |
| 59 | : QWaylandCompositorExtensionTemplate<QWaylandQtShell>(compositor, *new QWaylandQtShellPrivate()) |
| 60 | { |
| 61 | } |
| 62 | |
| 63 | bool QWaylandQtShell::moveChromeToFront(QWaylandQtShellChrome *chrome) |
| 64 | { |
| 65 | Q_D(QWaylandQtShell); |
| 66 | for (int i = 0; i < d->m_chromes.size(); ++i) { |
| 67 | if (d->m_chromes.at(i) == chrome) { |
| 68 | if (i > 0) { |
| 69 | QWaylandQtShellChrome *currentActive = d->m_chromes.first(); |
| 70 | d->m_chromes.move(i, 0); |
| 71 | chrome->activate(); |
| 72 | currentActive->deactivate(); |
| 73 | } |
| 74 | return true; |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | return false; |
| 79 | } |
| 80 | |
| 81 | void QWaylandQtShell::registerChrome(QWaylandQtShellChrome *chrome) |
| 82 | { |
| 83 | Q_D(QWaylandQtShell); |
| 84 | if (moveChromeToFront(chrome)) |
| 85 | return; |
| 86 | |
| 87 | QWaylandQtShellChrome *currentActive = d->m_chromes.isEmpty() ? nullptr : d->m_chromes.first(); |
| 88 | |
| 89 | d->m_chromes.prepend(chrome); |
| 90 | chrome->activate(); |
| 91 | |
| 92 | if (currentActive != nullptr) |
| 93 | currentActive->deactivate(); |
| 94 | |
| 95 | connect(sender: chrome, signal: &QWaylandQtShellChrome::activated, context: this, slot: &QWaylandQtShell::chromeActivated); |
| 96 | connect(sender: chrome, signal: &QWaylandQtShellChrome::deactivated, context: this, slot: &QWaylandQtShell::chromeDeactivated); |
| 97 | } |
| 98 | |
| 99 | void QWaylandQtShell::unregisterChrome(QWaylandQtShellChrome *chrome) |
| 100 | { |
| 101 | Q_D(QWaylandQtShell); |
| 102 | |
| 103 | chrome->disconnect(receiver: this); |
| 104 | int index = d->m_chromes.indexOf(chrome); |
| 105 | if (index >= 0) { |
| 106 | d->m_chromes.removeAt(index); |
| 107 | if (index == 0 && d->m_chromes.size() > 0) |
| 108 | d->m_chromes.at(0)->activate(); |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | void QWaylandQtShell::chromeActivated() |
| 113 | { |
| 114 | QWaylandQtShellChrome *c = qobject_cast<QWaylandQtShellChrome *>(object: sender()); |
| 115 | if (c != nullptr) { |
| 116 | moveChromeToFront(chrome: c); |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | void QWaylandQtShell::chromeDeactivated() |
| 121 | { |
| 122 | Q_D(QWaylandQtShell); |
| 123 | QWaylandQtShellChrome *c = qobject_cast<QWaylandQtShellChrome *>(object: sender()); |
| 124 | if (d->m_chromes.size() > 1 && d->m_chromes.at(0) == c) { |
| 125 | d->m_chromes.move(0, 1); |
| 126 | d->m_chromes.at(0)->activate(); |
| 127 | } else if (d->m_chromes.size() == 1) { // One window must be active |
| 128 | d->m_chromes.at(0)->activate(); |
| 129 | } |
| 130 | } |
| 131 | |
| 132 | void QWaylandQtShell::initialize() |
| 133 | { |
| 134 | Q_D(QWaylandQtShell); |
| 135 | QWaylandCompositorExtensionTemplate::initialize(); |
| 136 | |
| 137 | QWaylandCompositor *compositor = static_cast<QWaylandCompositor *>(extensionContainer()); |
| 138 | if (!compositor) { |
| 139 | qWarning() << "Failed to find QWaylandCompositor when initializing QWaylandQtShell" ; |
| 140 | return; |
| 141 | } |
| 142 | |
| 143 | d->init(compositor->display(), 1); |
| 144 | } |
| 145 | |
| 146 | const struct wl_interface *QWaylandQtShell::interface() |
| 147 | { |
| 148 | return QWaylandQtShellPrivate::interface(); |
| 149 | } |
| 150 | |
| 151 | /*! |
| 152 | * \internal |
| 153 | */ |
| 154 | QByteArray QWaylandQtShell::interfaceName() |
| 155 | { |
| 156 | return QWaylandQtShellPrivate::interfaceName(); |
| 157 | } |
| 158 | |
| 159 | /*! |
| 160 | * \qmlsignal void QtShell::qtShellSurfaceRequested(WaylandSurface surface, WaylandResource resource) |
| 161 | * |
| 162 | * This signal is emitted when the client has requested a QtShellSurface to be associated |
| 163 | * with \a surface. The handler for this signal is expected to create the QtShellSurface for |
| 164 | * \a resource and initialize it within the scope of the signal emission. If no QtShellSurface is |
| 165 | * created, a default one will be created instead. |
| 166 | */ |
| 167 | |
| 168 | /*! |
| 169 | * \qmlsignal void QtShell::qtShellSurfaceCreated(QtShellSurface *qtShellSurface) |
| 170 | * |
| 171 | * This signal is emitted when an QtShellSurface has been created. The supplied \a qtShellSurface is |
| 172 | * most commonly used to instantiate a ShellSurfaceItem. |
| 173 | */ |
| 174 | |
| 175 | QWaylandQtShellPrivate::QWaylandQtShellPrivate() |
| 176 | { |
| 177 | } |
| 178 | |
| 179 | void QWaylandQtShellPrivate::unregisterQtShellSurface(QWaylandQtShellSurface *qtShellSurface) |
| 180 | { |
| 181 | Q_UNUSED(qtShellSurface) |
| 182 | } |
| 183 | |
| 184 | void QWaylandQtShellPrivate::zqt_shell_v1_surface_create(QtWaylandServer::zqt_shell_v1::Resource *resource, wl_resource *surfaceResource, uint32_t id) |
| 185 | { |
| 186 | Q_Q(QWaylandQtShell); |
| 187 | QWaylandSurface *surface = QWaylandSurface::fromResource(resource: surfaceResource); |
| 188 | |
| 189 | if (!surface->setRole(role: QWaylandQtShellSurface::role(), errorResource: resource->handle, errorCode: ZQT_SHELL_V1_ERROR_ROLE)) |
| 190 | return; |
| 191 | |
| 192 | QWaylandResource qtShellSurfaceResource(wl_resource_create(resource->client(), &zqt_shell_surface_v1_interface, |
| 193 | wl_resource_get_version(resource->handle), id)); |
| 194 | |
| 195 | emit q->qtShellSurfaceRequested(surface, resource: qtShellSurfaceResource); |
| 196 | |
| 197 | QWaylandQtShellSurface *qtShellSurface = QWaylandQtShellSurface::fromResource(resource: qtShellSurfaceResource.resource()); |
| 198 | |
| 199 | if (!qtShellSurface) |
| 200 | qtShellSurface = new QWaylandQtShellSurface(q, surface, qtShellSurfaceResource); |
| 201 | |
| 202 | emit q->qtShellSurfaceCreated(qtShellSurface); |
| 203 | } |
| 204 | |
| 205 | QWaylandSurfaceRole QWaylandQtShellSurfacePrivate::s_role("qt_shell_surface" ); |
| 206 | |
| 207 | /*! |
| 208 | * \qmltype QtShellSurface |
| 209 | * \nativetype QWaylandQtShellSurface |
| 210 | * \inqmlmodule QtWayland.Compositor.QtShell |
| 211 | * \since 6.3 |
| 212 | * \brief Provides a simple way to identify and resize a surface. |
| 213 | * |
| 214 | * This type is part of the \l{QtShell} extension and provides a way to extend |
| 215 | * the functionality of an existing WaylandSurface with window management functionality. |
| 216 | * |
| 217 | * The QtShellSurface type holds the core functionality needed to create a compositor that supports |
| 218 | * the QtShell extension. It can be used directly, or via the QtShellChrome type, depending on what |
| 219 | * the needs of the compositor are. The QtShellChrome type has default behaviors and convenience |
| 220 | * APIs for working with QtShellSurface objects. |
| 221 | */ |
| 222 | |
| 223 | /*! |
| 224 | \qmlsignal void QtShellSurface::startMove() |
| 225 | |
| 226 | The client has requested an interactive move operation in the compositor by calling |
| 227 | \l{QWindow::startSystemMove()}. |
| 228 | |
| 229 | \sa capabilities |
| 230 | */ |
| 231 | |
| 232 | /*! |
| 233 | \qmlsignal void QtShellSurface::startResize(enum edges) |
| 234 | |
| 235 | The client has requested an interactive resize operation in the compositor by calling |
| 236 | \l{QWindow::startSystemResize()}. |
| 237 | |
| 238 | The \a edges provides information about which edge of the window should be moved during the |
| 239 | resize. It is a mask of the following values: |
| 240 | \list |
| 241 | \li Qt.TopEdge |
| 242 | \li Qt.LeftEdge |
| 243 | \li Qt.RightEdge |
| 244 | \li Qt.BottomEdge |
| 245 | \endlist |
| 246 | |
| 247 | \sa capabilities |
| 248 | */ |
| 249 | |
| 250 | QWaylandQtShellSurface::QWaylandQtShellSurface() |
| 251 | : QWaylandShellSurfaceTemplate<QWaylandQtShellSurface>(*new QWaylandQtShellSurfacePrivate()) |
| 252 | { |
| 253 | } |
| 254 | |
| 255 | QWaylandQtShellSurface::QWaylandQtShellSurface(QWaylandQtShell *application, QWaylandSurface *surface, const QWaylandResource &resource) |
| 256 | : QWaylandShellSurfaceTemplate<QWaylandQtShellSurface>(*new QWaylandQtShellSurfacePrivate()) |
| 257 | { |
| 258 | initialize(qtShell: application, surface, resource); |
| 259 | } |
| 260 | |
| 261 | /*! |
| 262 | * \qmlmethod void QtShellSurface::initialize(QtShell qtShell, WaylandSurface surface, WaylandResource resource) |
| 263 | * |
| 264 | * Initializes the QtShellSurface, associating it with the given \a qtShell, \a surface, and |
| 265 | * \a resource. |
| 266 | */ |
| 267 | void QWaylandQtShellSurface::initialize(QWaylandQtShell *qtShell, QWaylandSurface *surface, const QWaylandResource &resource) |
| 268 | { |
| 269 | Q_D(QWaylandQtShellSurface); |
| 270 | |
| 271 | d->m_qtShell = qtShell; |
| 272 | d->m_surface = surface; |
| 273 | |
| 274 | connect(sender: d->m_surface, signal: &QWaylandSurface::damaged, context: this, slot: &QWaylandQtShellSurface::surfaceCommitted); |
| 275 | |
| 276 | d->init(resource.resource()); |
| 277 | setExtensionContainer(surface); |
| 278 | |
| 279 | emit surfaceChanged(); |
| 280 | |
| 281 | QWaylandCompositorExtension::initialize(); |
| 282 | } |
| 283 | |
| 284 | /*! |
| 285 | * \qmlproperty WaylandSurface QtShellSurface::surface |
| 286 | * |
| 287 | * This property holds the surface associated with this QtShellSurface. |
| 288 | */ |
| 289 | QWaylandSurface *QWaylandQtShellSurface::surface() const |
| 290 | { |
| 291 | Q_D(const QWaylandQtShellSurface); |
| 292 | return d->m_surface; |
| 293 | } |
| 294 | |
| 295 | QWaylandQtShell *QWaylandQtShellSurface::shell() const |
| 296 | { |
| 297 | Q_D(const QWaylandQtShellSurface); |
| 298 | return d->m_qtShell; |
| 299 | } |
| 300 | |
| 301 | /*! |
| 302 | * \qmlproperty point QtShellSurface::windowPosition |
| 303 | * |
| 304 | * This property holds the position of the shell surface relative to its output. |
| 305 | */ |
| 306 | QPoint QWaylandQtShellSurface::windowPosition() const |
| 307 | { |
| 308 | Q_D(const QWaylandQtShellSurface); |
| 309 | return d->m_windowGeometry.topLeft(); |
| 310 | } |
| 311 | |
| 312 | void QWaylandQtShellSurface::setWindowPosition(const QPoint &position) |
| 313 | { |
| 314 | Q_D(QWaylandQtShellSurface); |
| 315 | |
| 316 | // We don't care about the ack in this case, so use UINT_MAX as serial |
| 317 | d->send_set_position(UINT32_MAX, position.x(), position.y()); |
| 318 | d->send_configure(UINT32_MAX); |
| 319 | |
| 320 | d->m_windowGeometry.moveTopLeft(p: position); |
| 321 | d->m_positionSet = true; |
| 322 | emit positionAutomaticChanged(); |
| 323 | emit windowGeometryChanged(); |
| 324 | } |
| 325 | |
| 326 | /*! |
| 327 | * \qmlproperty rect QtShellSurface::windowGeometry |
| 328 | * |
| 329 | * This property holds the window geometry of the shell surface. |
| 330 | */ |
| 331 | QRect QWaylandQtShellSurface::windowGeometry() const |
| 332 | { |
| 333 | Q_D(const QWaylandQtShellSurface); |
| 334 | return d->m_windowGeometry; |
| 335 | } |
| 336 | |
| 337 | /*! |
| 338 | * \qmlproperty size QtShellSurface::minimumSize |
| 339 | * |
| 340 | * The minimum size of the window if the client has specified one. Otherwise an invalid size. |
| 341 | */ |
| 342 | QSize QWaylandQtShellSurface::minimumSize() const |
| 343 | { |
| 344 | Q_D(const QWaylandQtShellSurface); |
| 345 | return d->m_minimumSize; |
| 346 | } |
| 347 | |
| 348 | /*! |
| 349 | * \qmlproperty size QtShellSurface::maximumSize |
| 350 | * |
| 351 | * The maximum size of the window if the client has specified one. Otherwise an invalid size. |
| 352 | */ |
| 353 | QSize QWaylandQtShellSurface::maximumSize() const |
| 354 | { |
| 355 | Q_D(const QWaylandQtShellSurface); |
| 356 | return d->m_maximumSize; |
| 357 | } |
| 358 | |
| 359 | /*! |
| 360 | * \qmlmethod void QtShellSurface::requestWindowGeometry(int windowState, rect windowGeometry) |
| 361 | * |
| 362 | * Requests a new \a windowState and \a windowGeometry for the QtShellSurface. The state and |
| 363 | * geometry is updated when the client has acknowledged the request (at which point it is safe to |
| 364 | * assume that the surface's buffer has been resized if necessary). |
| 365 | */ |
| 366 | void QWaylandQtShellSurface::requestWindowGeometry(uint windowState, const QRect &windowGeometry) |
| 367 | { |
| 368 | Q_D(QWaylandQtShellSurface); |
| 369 | if (!windowGeometry.isValid()) |
| 370 | return; |
| 371 | |
| 372 | d->configure(windowState, newGeometry: windowGeometry); |
| 373 | } |
| 374 | |
| 375 | void QWaylandQtShellSurfacePrivate::configure(uint windowState, const QRect &newGeometry) |
| 376 | { |
| 377 | QWaylandCompositor *compositor = m_surface != nullptr ? m_surface->compositor() : nullptr; |
| 378 | if (!compositor) { |
| 379 | qWarning() << "Failed to find QWaylandCompositor when configuring QWaylandQtShell" ; |
| 380 | return; |
| 381 | } |
| 382 | |
| 383 | uint32_t serial = compositor->nextSerial(); |
| 384 | m_pendingConfigures[serial] = qMakePair(value1&: windowState, value2: newGeometry); |
| 385 | |
| 386 | send_set_position(serial, newGeometry.x(), newGeometry.y()); |
| 387 | send_resize(serial, newGeometry.width(), newGeometry.height()); |
| 388 | send_set_window_state(serial, windowState & ~Qt::WindowActive); |
| 389 | send_configure(serial); |
| 390 | } |
| 391 | |
| 392 | void QWaylandQtShellSurface::setFrameMargins(const QMargins &margins) |
| 393 | { |
| 394 | Q_D(QWaylandQtShellSurface); |
| 395 | if (d->m_frameMargins == margins) |
| 396 | return; |
| 397 | |
| 398 | d->m_frameMargins = margins; |
| 399 | d->updateFrameMargins(); |
| 400 | |
| 401 | emit frameMarginChanged(); |
| 402 | } |
| 403 | |
| 404 | /*! |
| 405 | * \qmlproperty int QtShellSurface::frameMarginLeft |
| 406 | * |
| 407 | * This holds the window frame margin to the left of the surface. |
| 408 | */ |
| 409 | void QWaylandQtShellSurface::setFrameMarginLeft(int left) |
| 410 | { |
| 411 | Q_D(QWaylandQtShellSurface); |
| 412 | if (d->m_frameMargins.left() == left) |
| 413 | return; |
| 414 | |
| 415 | d->m_frameMargins.setLeft(left); |
| 416 | d->updateFrameMargins(); |
| 417 | |
| 418 | emit frameMarginChanged(); |
| 419 | } |
| 420 | |
| 421 | int QWaylandQtShellSurface::frameMarginLeft() const |
| 422 | { |
| 423 | Q_D(const QWaylandQtShellSurface); |
| 424 | return d->m_frameMargins.left(); |
| 425 | } |
| 426 | |
| 427 | /*! |
| 428 | * \qmlproperty int QtShellSurface::frameMarginRight |
| 429 | * |
| 430 | * This holds the window frame margin to the right of the surface. |
| 431 | */ |
| 432 | void QWaylandQtShellSurface::setFrameMarginRight(int right) |
| 433 | { |
| 434 | Q_D(QWaylandQtShellSurface); |
| 435 | if (d->m_frameMargins.right() == right) |
| 436 | return; |
| 437 | |
| 438 | d->m_frameMargins.setRight(right); |
| 439 | d->updateFrameMargins(); |
| 440 | |
| 441 | emit frameMarginChanged(); |
| 442 | } |
| 443 | |
| 444 | int QWaylandQtShellSurface::frameMarginRight() const |
| 445 | { |
| 446 | Q_D(const QWaylandQtShellSurface); |
| 447 | return d->m_frameMargins.right(); |
| 448 | } |
| 449 | |
| 450 | /*! |
| 451 | * \qmlproperty int QtShellSurface::frameMarginTop |
| 452 | * |
| 453 | * This holds the window frame margin above the surface. |
| 454 | */ |
| 455 | |
| 456 | void QWaylandQtShellSurface::setFrameMarginTop(int top) |
| 457 | { |
| 458 | Q_D(QWaylandQtShellSurface); |
| 459 | if (d->m_frameMargins.top() == top) |
| 460 | return; |
| 461 | d->m_frameMargins.setTop(top); |
| 462 | d->updateFrameMargins(); |
| 463 | |
| 464 | emit frameMarginChanged(); |
| 465 | } |
| 466 | |
| 467 | int QWaylandQtShellSurface::frameMarginTop() const |
| 468 | { |
| 469 | Q_D(const QWaylandQtShellSurface); |
| 470 | return d->m_frameMargins.top(); |
| 471 | } |
| 472 | |
| 473 | /*! |
| 474 | * \qmlproperty int QtShellSurface::frameMarginBottom |
| 475 | * |
| 476 | * This holds the window frame margin below the surface. |
| 477 | */ |
| 478 | void QWaylandQtShellSurface::setFrameMarginBottom(int bottom) |
| 479 | { |
| 480 | Q_D(QWaylandQtShellSurface); |
| 481 | if (d->m_frameMargins.bottom() == bottom) |
| 482 | return; |
| 483 | d->m_frameMargins.setBottom(bottom); |
| 484 | d->updateFrameMargins(); |
| 485 | |
| 486 | emit frameMarginChanged(); |
| 487 | } |
| 488 | |
| 489 | bool QWaylandQtShellSurface::positionAutomatic() const |
| 490 | { |
| 491 | Q_D(const QWaylandQtShellSurface); |
| 492 | return !d->m_positionSet; |
| 493 | } |
| 494 | |
| 495 | int QWaylandQtShellSurface::frameMarginBottom() const |
| 496 | { |
| 497 | Q_D(const QWaylandQtShellSurface); |
| 498 | return d->m_frameMargins.bottom(); |
| 499 | } |
| 500 | |
| 501 | /*! |
| 502 | * \qmlproperty int QtShellSurface::windowFlags |
| 503 | * |
| 504 | * This property holds the window flags of the QtShellSurface. |
| 505 | */ |
| 506 | uint QWaylandQtShellSurface::windowFlags() const |
| 507 | { |
| 508 | Q_D(const QWaylandQtShellSurface); |
| 509 | return d->m_windowFlags; |
| 510 | } |
| 511 | |
| 512 | /*! |
| 513 | * \qmlmethod void QtShellSurface::sendClose() |
| 514 | * |
| 515 | * Requests that the client application closes itself. |
| 516 | */ |
| 517 | void QWaylandQtShellSurface::sendClose() |
| 518 | { |
| 519 | Q_D(QWaylandQtShellSurface); |
| 520 | d->send_close(); |
| 521 | } |
| 522 | |
| 523 | /*! |
| 524 | * \qmlproperty string QtShellSurface::windowTitle |
| 525 | * |
| 526 | * This property holds the window title of the QtShellSurface. |
| 527 | */ |
| 528 | QString QWaylandQtShellSurface::windowTitle() const |
| 529 | { |
| 530 | Q_D(const QWaylandQtShellSurface); |
| 531 | return d->m_windowTitle; |
| 532 | } |
| 533 | |
| 534 | /*! |
| 535 | * \qmlproperty bool QtShellSurface::active |
| 536 | * |
| 537 | * This property holds whether the surface is currently considered active. |
| 538 | * |
| 539 | * \note There are no restrictions in QtShellSurface that prevents multiple surfaces from being |
| 540 | * active simultaneously. Such logic must either be implemented by the compositor itself, or by |
| 541 | * using the QtShellChrome type, which will automatically manage the activation state of surfaces. |
| 542 | */ |
| 543 | void QWaylandQtShellSurface::setActive(bool active) |
| 544 | { |
| 545 | Q_D(QWaylandQtShellSurface); |
| 546 | if (d->m_active == active) |
| 547 | return; |
| 548 | |
| 549 | d->m_active = active; |
| 550 | QWaylandCompositor *compositor = d->m_surface ? d->m_surface->compositor() : nullptr; |
| 551 | QWaylandSeat *seat = compositor ? compositor->defaultSeat() : nullptr; |
| 552 | if (seat && active) |
| 553 | seat->setKeyboardFocus(surface()); |
| 554 | emit activeChanged(); |
| 555 | } |
| 556 | |
| 557 | bool QWaylandQtShellSurface::active() const |
| 558 | { |
| 559 | Q_D(const QWaylandQtShellSurface); |
| 560 | return d->m_active; |
| 561 | } |
| 562 | |
| 563 | /*! |
| 564 | * \qmlproperty enum QtShellSurface::capabilities |
| 565 | * |
| 566 | * This property holds the capabilities of the compositor. By default, no special capabilities are |
| 567 | * enabled. |
| 568 | * |
| 569 | * \list |
| 570 | * \li QtShellSurface.InteractiveMove The client can trigger a server-side interactive move |
| 571 | * operation using \l{QWindow::startSystemMove()}. The compositor will be notified of this |
| 572 | * through the \l{startMove()} signal. |
| 573 | * \li QtShellSurface.InteractiveResize The client can trigger a server-side interactive resize |
| 574 | * operation using \l{QWindow::startSystemResize()}. The compositor will be notified of this |
| 575 | * through the \l{startResize()} signal. |
| 576 | * \endlist |
| 577 | */ |
| 578 | void QWaylandQtShellSurface::setCapabilities(CapabilityFlags capabilities) |
| 579 | { |
| 580 | Q_D(QWaylandQtShellSurface); |
| 581 | if (d->m_capabilities == capabilities) |
| 582 | return; |
| 583 | |
| 584 | d->m_capabilities = capabilities; |
| 585 | d->send_set_capabilities(capabilities); |
| 586 | |
| 587 | emit capabilitiesChanged(); |
| 588 | } |
| 589 | |
| 590 | QWaylandQtShellSurface::CapabilityFlags QWaylandQtShellSurface::capabilities() const |
| 591 | { |
| 592 | Q_D(const QWaylandQtShellSurface); |
| 593 | return d->m_capabilities; |
| 594 | } |
| 595 | |
| 596 | /*! |
| 597 | * \qmlproperty int QtShellSurface::windowState |
| 598 | * |
| 599 | * This property holds the window state of the QtShellSurface. |
| 600 | * |
| 601 | * \note When \l{requestWindowGeometry()} is called to update state of the surface, the |
| 602 | * \c windowState property will not be updated until the client has acknowledged the state change. |
| 603 | */ |
| 604 | uint QWaylandQtShellSurface::windowState() const |
| 605 | { |
| 606 | Q_D(const QWaylandQtShellSurface); |
| 607 | return d->m_windowState; |
| 608 | } |
| 609 | |
| 610 | void QWaylandQtShellSurface::surfaceCommitted() |
| 611 | { |
| 612 | Q_D(QWaylandQtShellSurface); |
| 613 | if (d->m_lastAckedConfigure < UINT32_MAX) { |
| 614 | QRect targetRect = d->m_windowGeometry; |
| 615 | uint windowState = d->m_windowState; |
| 616 | for (auto it = d->m_pendingConfigures.begin(); it != d->m_pendingConfigures.end(); ) { |
| 617 | if (it.key() == d->m_lastAckedConfigure) { |
| 618 | targetRect = it.value().second; |
| 619 | windowState = it.value().first; |
| 620 | } |
| 621 | |
| 622 | if (it.key() <= d->m_lastAckedConfigure) |
| 623 | it = d->m_pendingConfigures.erase(it); |
| 624 | else |
| 625 | break; |
| 626 | } |
| 627 | |
| 628 | if (d->m_windowState != windowState) { |
| 629 | d->m_windowState = windowState; |
| 630 | emit windowStateChanged(); |
| 631 | } |
| 632 | |
| 633 | if (d->m_windowGeometry != targetRect) { |
| 634 | d->m_windowGeometry = targetRect; |
| 635 | d->m_positionSet = true; |
| 636 | emit positionAutomaticChanged(); |
| 637 | emit windowGeometryChanged(); |
| 638 | } |
| 639 | |
| 640 | d->m_lastAckedConfigure = UINT32_MAX; |
| 641 | d->m_pendingPosition = QPoint{}; |
| 642 | d->m_pendingPositionValid = false; |
| 643 | d->m_pendingSize = QSize{}; |
| 644 | } else { |
| 645 | QRect oldRect = d->m_windowGeometry; |
| 646 | if (d->m_pendingPositionValid) { |
| 647 | d->m_windowGeometry.moveTopLeft(p: d->m_pendingPosition); |
| 648 | d->m_pendingPosition = QPoint{}; |
| 649 | d->m_pendingPositionValid = false; |
| 650 | d->m_positionSet = true; |
| 651 | emit positionAutomaticChanged(); |
| 652 | } |
| 653 | |
| 654 | if (d->m_pendingSize.isValid()) { |
| 655 | d->m_windowGeometry.setSize(d->m_pendingSize); |
| 656 | d->m_pendingSize = QSize{}; |
| 657 | } |
| 658 | |
| 659 | if (d->m_windowGeometry != oldRect) |
| 660 | emit windowGeometryChanged(); |
| 661 | } |
| 662 | } |
| 663 | |
| 664 | /*! |
| 665 | * Returns the Wayland interface for the QWaylandQtShellSurface. |
| 666 | */ |
| 667 | const wl_interface *QWaylandQtShellSurface::interface() |
| 668 | { |
| 669 | return QWaylandQtShellSurfacePrivate::interface(); |
| 670 | } |
| 671 | |
| 672 | QByteArray QWaylandQtShellSurface::interfaceName() |
| 673 | { |
| 674 | return QWaylandQtShellSurfacePrivate::interfaceName(); |
| 675 | } |
| 676 | |
| 677 | /*! |
| 678 | * Returns the surface role for the QWaylandQtShellSurface. |
| 679 | */ |
| 680 | QWaylandSurfaceRole *QWaylandQtShellSurface::role() |
| 681 | { |
| 682 | return &QWaylandQtShellSurfacePrivate::s_role; |
| 683 | } |
| 684 | |
| 685 | /*! |
| 686 | * Returns the QWaylandQtShellSurface corresponding to the \a resource. |
| 687 | */ |
| 688 | QWaylandQtShellSurface *QWaylandQtShellSurface::fromResource(wl_resource *resource) |
| 689 | { |
| 690 | if (auto p = QtWayland::fromResource<QWaylandQtShellSurfacePrivate *>(resource)) |
| 691 | return p->q_func(); |
| 692 | return nullptr; |
| 693 | } |
| 694 | |
| 695 | #if QT_CONFIG(wayland_compositor_quick) |
| 696 | QWaylandQuickShellIntegration *QWaylandQtShellSurface::createIntegration(QWaylandQuickShellSurfaceItem *item) |
| 697 | { |
| 698 | return new QtWayland::QtShellIntegration(item); |
| 699 | } |
| 700 | #endif |
| 701 | |
| 702 | /*! |
| 703 | * \internal |
| 704 | */ |
| 705 | void QWaylandQtShellSurface::initialize() |
| 706 | { |
| 707 | QWaylandShellSurfaceTemplate::initialize(); |
| 708 | } |
| 709 | |
| 710 | QWaylandQtShellSurfacePrivate::QWaylandQtShellSurfacePrivate() |
| 711 | { |
| 712 | } |
| 713 | |
| 714 | void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_ack_configure(Resource *resource, uint32_t serial) |
| 715 | { |
| 716 | Q_UNUSED(resource); |
| 717 | Q_Q(QWaylandQtShellSurface); |
| 718 | if (serial < UINT32_MAX) |
| 719 | m_lastAckedConfigure = serial; |
| 720 | |
| 721 | // Fake a surface commit because we won't get one as long as the window is unexposed |
| 722 | if (m_windowState & Qt::WindowMinimized) |
| 723 | q->surfaceCommitted(); |
| 724 | } |
| 725 | |
| 726 | void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_reposition(Resource *resource, int32_t x, int32_t y) |
| 727 | { |
| 728 | Q_UNUSED(resource); |
| 729 | |
| 730 | m_pendingPosition = QPoint(x, y); |
| 731 | m_pendingPositionValid = true; |
| 732 | m_lastAckedConfigure = UINT32_MAX; |
| 733 | } |
| 734 | |
| 735 | void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_set_size(Resource *resource, int32_t width, int32_t height) |
| 736 | { |
| 737 | Q_UNUSED(resource); |
| 738 | |
| 739 | m_pendingSize = QSize(width, height); |
| 740 | m_lastAckedConfigure = UINT32_MAX; |
| 741 | } |
| 742 | |
| 743 | void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_set_minimum_size(Resource *resource, int32_t width, int32_t height) |
| 744 | { |
| 745 | Q_UNUSED(resource); |
| 746 | Q_Q(QWaylandQtShellSurface); |
| 747 | m_minimumSize = QSize{width, height}; |
| 748 | emit q->minimumSizeChanged(); |
| 749 | } |
| 750 | |
| 751 | void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_set_maximum_size(Resource *resource, int32_t width, int32_t height) |
| 752 | { |
| 753 | Q_UNUSED(resource); |
| 754 | Q_Q(QWaylandQtShellSurface); |
| 755 | m_maximumSize = QSize{width, height}; |
| 756 | emit q->maximumSizeChanged(); |
| 757 | } |
| 758 | |
| 759 | void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_destroy_resource(QtWaylandServer::zqt_shell_surface_v1::Resource *resource) |
| 760 | { |
| 761 | Q_UNUSED(resource); |
| 762 | Q_Q(QWaylandQtShellSurface); |
| 763 | QWaylandQtShellPrivate::get(qtShell: m_qtShell)->unregisterQtShellSurface(qtShellSurface: q); |
| 764 | delete q; |
| 765 | } |
| 766 | |
| 767 | void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_destroy(QtWaylandServer::zqt_shell_surface_v1::Resource *resource) |
| 768 | { |
| 769 | wl_resource_destroy(resource->handle); |
| 770 | } |
| 771 | |
| 772 | void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_set_window_flags(Resource *resource, uint32_t flags) |
| 773 | { |
| 774 | Q_UNUSED(resource); |
| 775 | Q_Q(QWaylandQtShellSurface); |
| 776 | m_windowFlags = flags; |
| 777 | emit q->windowFlagsChanged(); |
| 778 | } |
| 779 | |
| 780 | void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_change_window_state(Resource *resource, uint32_t state) |
| 781 | { |
| 782 | Q_UNUSED(resource); |
| 783 | Q_Q(QWaylandQtShellSurface); |
| 784 | uint oldWindowState = m_windowState; |
| 785 | m_windowState = state & ~Qt::WindowActive; |
| 786 | |
| 787 | if (oldWindowState != m_windowState) |
| 788 | emit q->windowStateChanged(); |
| 789 | } |
| 790 | |
| 791 | void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_start_system_resize(Resource *resource, uint32_t serial, uint32_t edge) |
| 792 | { |
| 793 | Q_UNUSED(resource); |
| 794 | Q_UNUSED(serial); |
| 795 | Q_Q(QWaylandQtShellSurface); |
| 796 | emit q->startResize(edges: Qt::Edges(edge)); |
| 797 | } |
| 798 | |
| 799 | void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_start_system_move(Resource *resource, uint32_t serial) |
| 800 | { |
| 801 | Q_UNUSED(resource); |
| 802 | Q_UNUSED(serial); |
| 803 | Q_Q(QWaylandQtShellSurface); |
| 804 | emit q->startMove(); |
| 805 | } |
| 806 | |
| 807 | void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_set_window_title(Resource *resource, |
| 808 | const QString &title) |
| 809 | { |
| 810 | Q_UNUSED(resource); |
| 811 | Q_Q(QWaylandQtShellSurface); |
| 812 | m_windowTitle = title; |
| 813 | emit q->windowTitleChanged(); |
| 814 | } |
| 815 | |
| 816 | void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_request_activate(Resource *resource) |
| 817 | |
| 818 | { |
| 819 | Q_UNUSED(resource); |
| 820 | Q_Q(QWaylandQtShellSurface); |
| 821 | q->setActive(true); |
| 822 | |
| 823 | if (m_surface) { |
| 824 | auto view = m_surface->primaryView(); |
| 825 | auto item = view ? qobject_cast<QWaylandQuickItem *>(object: view->renderObject()) : nullptr; |
| 826 | if (item) |
| 827 | item->forceActiveFocus(); |
| 828 | } |
| 829 | } |
| 830 | |
| 831 | void QWaylandQtShellSurfacePrivate::updateFrameMargins() |
| 832 | { |
| 833 | send_set_frame_margins(m_frameMargins.left(), m_frameMargins.right(), |
| 834 | m_frameMargins.top(), m_frameMargins.bottom()); |
| 835 | } |
| 836 | |
| 837 | |
| 838 | void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_raise(Resource *resource) |
| 839 | { |
| 840 | Q_UNUSED(resource); |
| 841 | Q_Q(QWaylandQtShellSurface); |
| 842 | emit q->raiseRequested(); |
| 843 | } |
| 844 | |
| 845 | void QWaylandQtShellSurfacePrivate::zqt_shell_surface_v1_lower(Resource *resource) |
| 846 | { |
| 847 | Q_UNUSED(resource); |
| 848 | Q_Q(QWaylandQtShellSurface); |
| 849 | emit q->lowerRequested(); |
| 850 | } |
| 851 | |
| 852 | QT_END_NAMESPACE |
| 853 | |
| 854 | #include "moc_qwaylandqtshell_p.cpp" |
| 855 | |