File indexing completed on 2024-11-10 04:57:37
0001 /* 0002 SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 0007 #include "xdgshell.h" 0008 #include "xdgshell_p.h" 0009 0010 #include "display.h" 0011 #include "output.h" 0012 #include "seat.h" 0013 #include "utils/common.h" 0014 #include "utils/resource.h" 0015 0016 #include <QTimer> 0017 0018 namespace KWin 0019 { 0020 static const int s_version = 6; 0021 0022 XdgShellInterfacePrivate::XdgShellInterfacePrivate(XdgShellInterface *shell) 0023 : q(shell) 0024 { 0025 } 0026 0027 XdgShellInterfacePrivate::Resource *XdgShellInterfacePrivate::resourceForXdgSurface(XdgSurfaceInterface *surface) const 0028 { 0029 return xdgSurfaces.value(surface); 0030 } 0031 0032 void XdgShellInterfacePrivate::unregisterXdgSurface(XdgSurfaceInterface *surface) 0033 { 0034 xdgSurfaces.remove(surface); 0035 } 0036 0037 /** 0038 * @todo Whether the ping is delayed or has timed out is out of domain of the XdgShellInterface. 0039 * Such matter must be handled somewhere else, e.g. XdgToplevelWindow, not here! 0040 */ 0041 void XdgShellInterfacePrivate::registerPing(quint32 serial) 0042 { 0043 QTimer *timer = new QTimer(q); 0044 // we'll run the timer twice. 0045 timer->setInterval(pingTimeout / 2); 0046 QObject::connect(timer, &QTimer::timeout, q, [this, serial, attempt = 0]() mutable { 0047 ++attempt; 0048 if (attempt == 1) { 0049 Q_EMIT q->pingDelayed(serial); 0050 return; 0051 } 0052 Q_EMIT q->pingTimeout(serial); 0053 delete pings.take(serial); 0054 }); 0055 pings.insert(serial, timer); 0056 timer->start(); 0057 } 0058 0059 XdgShellInterfacePrivate *XdgShellInterfacePrivate::get(XdgShellInterface *shell) 0060 { 0061 return shell->d.get(); 0062 } 0063 0064 void XdgShellInterfacePrivate::xdg_wm_base_destroy_resource(Resource *resource) 0065 { 0066 const QList<XdgSurfaceInterface *> surfaces = xdgSurfaces.keys(resource); 0067 qDeleteAll(surfaces); 0068 } 0069 0070 void XdgShellInterfacePrivate::xdg_wm_base_destroy(Resource *resource) 0071 { 0072 if (xdgSurfaces.key(resource)) { 0073 wl_resource_post_error(resource->handle, error_defunct_surfaces, "xdg_wm_base was destroyed before children"); 0074 return; 0075 } 0076 wl_resource_destroy(resource->handle); 0077 } 0078 0079 void XdgShellInterfacePrivate::xdg_wm_base_create_positioner(Resource *resource, uint32_t id) 0080 { 0081 wl_resource *positionerResource = wl_resource_create(resource->client(), &xdg_positioner_interface, resource->version(), id); 0082 new XdgPositionerPrivate(positionerResource); 0083 } 0084 0085 void XdgShellInterfacePrivate::xdg_wm_base_get_xdg_surface(Resource *resource, uint32_t id, ::wl_resource *surfaceResource) 0086 { 0087 SurfaceInterface *surface = SurfaceInterface::get(surfaceResource); 0088 0089 if (surface->buffer()) { 0090 wl_resource_post_error(resource->handle, XDG_SURFACE_ERROR_UNCONFIGURED_BUFFER, "xdg_surface must not have a buffer at creation"); 0091 return; 0092 } 0093 0094 wl_resource *xdgSurfaceResource = wl_resource_create(resource->client(), &xdg_surface_interface, resource->version(), id); 0095 0096 XdgSurfaceInterface *xdgSurface = new XdgSurfaceInterface(q, surface, xdgSurfaceResource); 0097 xdgSurfaces.insert(xdgSurface, resource); 0098 } 0099 0100 void XdgShellInterfacePrivate::xdg_wm_base_pong(Resource *resource, uint32_t serial) 0101 { 0102 if (QTimer *timer = pings.take(serial)) { 0103 delete timer; 0104 } 0105 Q_EMIT q->pongReceived(serial); 0106 } 0107 0108 XdgShellInterface::XdgShellInterface(Display *display, QObject *parent) 0109 : QObject(parent) 0110 , d(new XdgShellInterfacePrivate(this)) 0111 { 0112 d->display = display; 0113 d->init(*display, s_version); 0114 } 0115 0116 XdgShellInterface::~XdgShellInterface() 0117 { 0118 } 0119 0120 Display *XdgShellInterface::display() const 0121 { 0122 return d->display; 0123 } 0124 0125 quint32 XdgShellInterface::ping(XdgSurfaceInterface *surface) 0126 { 0127 XdgShellInterfacePrivate::Resource *clientResource = d->resourceForXdgSurface(surface); 0128 if (!clientResource) 0129 return 0; 0130 0131 quint32 serial = d->display->nextSerial(); 0132 d->send_ping(clientResource->handle, serial); 0133 d->registerPing(serial); 0134 0135 return serial; 0136 } 0137 0138 std::chrono::milliseconds XdgShellInterface::pingTimeoutInterval() const 0139 { 0140 return d->pingTimeout; 0141 } 0142 0143 void XdgShellInterface::setPingTimeoutInterval(std::chrono::milliseconds pingTimeout) 0144 { 0145 d->pingTimeout = pingTimeout; 0146 } 0147 0148 XdgSurfaceInterfacePrivate::XdgSurfaceInterfacePrivate(XdgSurfaceInterface *xdgSurface) 0149 : q(xdgSurface) 0150 { 0151 } 0152 0153 void XdgSurfaceInterfacePrivate::apply(XdgSurfaceCommit *commit) 0154 { 0155 if (surface->buffer()) { 0156 firstBufferAttached = true; 0157 } 0158 0159 if (commit->acknowledgedConfigure.has_value()) { 0160 Q_EMIT q->configureAcknowledged(commit->acknowledgedConfigure.value()); 0161 } 0162 0163 if (commit->windowGeometry.has_value()) { 0164 windowGeometry = commit->windowGeometry.value(); 0165 Q_EMIT q->windowGeometryChanged(windowGeometry); 0166 } 0167 } 0168 0169 void XdgSurfaceInterfacePrivate::reset() 0170 { 0171 firstBufferAttached = false; 0172 isConfigured = false; 0173 isInitialized = false; 0174 windowGeometry = QRect(); 0175 Q_EMIT q->resetOccurred(); 0176 } 0177 0178 XdgSurfaceInterfacePrivate *XdgSurfaceInterfacePrivate::get(XdgSurfaceInterface *surface) 0179 { 0180 return surface->d.get(); 0181 } 0182 0183 void XdgSurfaceInterfacePrivate::xdg_surface_destroy_resource(Resource *resource) 0184 { 0185 delete q; 0186 } 0187 0188 void XdgSurfaceInterfacePrivate::xdg_surface_destroy(Resource *resource) 0189 { 0190 if (toplevel || popup) { 0191 qWarning() << "Tried to destroy xdg_surface before its role object"; 0192 } 0193 wl_resource_destroy(resource->handle); 0194 } 0195 0196 void XdgSurfaceInterfacePrivate::xdg_surface_get_toplevel(Resource *resource, uint32_t id) 0197 { 0198 if (const SurfaceRole *role = surface->role()) { 0199 if (role != XdgToplevelInterface::role()) { 0200 wl_resource_post_error(resource->handle, error_already_constructed, "the surface already has a role assigned %s", role->name().constData()); 0201 return; 0202 } 0203 } else { 0204 surface->setRole(XdgToplevelInterface::role()); 0205 } 0206 0207 wl_resource *toplevelResource = wl_resource_create(resource->client(), &xdg_toplevel_interface, resource->version(), id); 0208 0209 auto toplevel = new XdgToplevelInterface(q, toplevelResource); 0210 Q_EMIT shell->toplevelCreated(toplevel); 0211 } 0212 0213 void XdgSurfaceInterfacePrivate::xdg_surface_get_popup(Resource *resource, uint32_t id, ::wl_resource *parentResource, ::wl_resource *positionerResource) 0214 { 0215 if (const SurfaceRole *role = surface->role()) { 0216 if (role != XdgPopupInterface::role()) { 0217 wl_resource_post_error(resource->handle, error_already_constructed, "the surface already has a role assigned %s", role->name().constData()); 0218 return; 0219 } 0220 } else { 0221 surface->setRole(XdgPopupInterface::role()); 0222 } 0223 0224 XdgPositioner positioner = XdgPositioner::get(positionerResource); 0225 if (!positioner.isComplete()) { 0226 auto shellPrivate = XdgShellInterfacePrivate::get(shell); 0227 wl_resource_post_error(shellPrivate->resourceForXdgSurface(q)->handle, 0228 QtWaylandServer::xdg_wm_base::error_invalid_positioner, 0229 "xdg_positioner is incomplete"); 0230 return; 0231 } 0232 0233 XdgSurfaceInterface *parentXdgSurface = XdgSurfaceInterface::get(parentResource); 0234 SurfaceInterface *parentSurface = nullptr; 0235 if (parentXdgSurface) { 0236 if (!parentXdgSurface->surface()->role()) { 0237 auto shellPrivate = XdgShellInterfacePrivate::get(shell); 0238 wl_resource_post_error(shellPrivate->resourceForXdgSurface(q)->handle, 0239 QtWaylandServer::xdg_wm_base::error_invalid_popup_parent, 0240 "parent surface has no surface role"); 0241 return; 0242 } 0243 parentSurface = parentXdgSurface->surface(); 0244 } 0245 0246 wl_resource *popupResource = wl_resource_create(resource->client(), &xdg_popup_interface, resource->version(), id); 0247 0248 auto popup = new XdgPopupInterface(q, parentSurface, positioner, popupResource); 0249 Q_EMIT shell->popupCreated(popup); 0250 } 0251 0252 void XdgSurfaceInterfacePrivate::xdg_surface_set_window_geometry(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) 0253 { 0254 if (Q_UNLIKELY(!pending)) { 0255 wl_resource_post_error(resource->handle, error_not_constructed, "xdg_surface must have a role"); 0256 return; 0257 } 0258 0259 if (width < 1 || height < 1) { 0260 wl_resource_post_error(resource->handle, -1, "invalid window geometry size (%dx%d)", width, height); 0261 return; 0262 } 0263 0264 pending->windowGeometry = QRect(x, y, width, height); 0265 } 0266 0267 void XdgSurfaceInterfacePrivate::xdg_surface_ack_configure(Resource *resource, uint32_t serial) 0268 { 0269 if (Q_UNLIKELY(!pending)) { 0270 wl_resource_post_error(resource->handle, error_not_constructed, "xdg_surface must have a role"); 0271 return; 0272 } 0273 0274 pending->acknowledgedConfigure = serial; 0275 } 0276 0277 XdgSurfaceInterface::XdgSurfaceInterface(XdgShellInterface *shell, SurfaceInterface *surface, ::wl_resource *resource) 0278 : d(new XdgSurfaceInterfacePrivate(this)) 0279 { 0280 d->shell = shell; 0281 d->surface = surface; 0282 d->init(resource); 0283 } 0284 0285 XdgSurfaceInterface::~XdgSurfaceInterface() 0286 { 0287 delete d->toplevel; 0288 delete d->popup; 0289 0290 Q_EMIT aboutToBeDestroyed(); 0291 XdgShellInterfacePrivate::get(d->shell)->unregisterXdgSurface(this); 0292 } 0293 0294 XdgToplevelInterface *XdgSurfaceInterface::toplevel() const 0295 { 0296 return d->toplevel; 0297 } 0298 0299 XdgPopupInterface *XdgSurfaceInterface::popup() const 0300 { 0301 return d->popup; 0302 } 0303 0304 XdgShellInterface *XdgSurfaceInterface::shell() const 0305 { 0306 return d->shell; 0307 } 0308 0309 SurfaceInterface *XdgSurfaceInterface::surface() const 0310 { 0311 return d->surface; 0312 } 0313 0314 bool XdgSurfaceInterface::isConfigured() const 0315 { 0316 return d->isConfigured; 0317 } 0318 0319 QRect XdgSurfaceInterface::windowGeometry() const 0320 { 0321 return d->windowGeometry; 0322 } 0323 0324 XdgSurfaceInterface *XdgSurfaceInterface::get(::wl_resource *resource) 0325 { 0326 if (auto surfacePrivate = resource_cast<XdgSurfaceInterfacePrivate *>(resource)) { 0327 return surfacePrivate->q; 0328 } 0329 return nullptr; 0330 } 0331 0332 XdgToplevelInterfacePrivate::XdgToplevelInterfacePrivate(XdgToplevelInterface *toplevel, XdgSurfaceInterface *xdgSurface) 0333 : SurfaceExtension(xdgSurface->surface()) 0334 , q(toplevel) 0335 , xdgSurface(xdgSurface) 0336 { 0337 } 0338 0339 void XdgToplevelInterfacePrivate::apply(XdgToplevelCommit *commit) 0340 { 0341 auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface); 0342 if (xdgSurfacePrivate->firstBufferAttached && !xdgSurfacePrivate->surface->buffer()) { 0343 reset(); 0344 return; 0345 } 0346 0347 xdgSurfacePrivate->apply(commit); 0348 0349 const auto minSize = commit->minimumSize.value_or(minimumSize); 0350 const auto maxSize = commit->maximumSize.value_or(maximumSize); 0351 if (!minSize.isEmpty() && !maxSize.isEmpty() && (minSize.width() > maxSize.width() || minSize.height() > maxSize.height())) { 0352 wl_resource_post_error(resource()->handle, error_invalid_size, "minimum size can't be bigger than the maximum"); 0353 return; 0354 } 0355 0356 if (commit->minimumSize && commit->minimumSize != minimumSize) { 0357 minimumSize = commit->minimumSize.value(); 0358 Q_EMIT q->minimumSizeChanged(minimumSize); 0359 } 0360 if (commit->maximumSize && commit->maximumSize != maximumSize) { 0361 maximumSize = commit->maximumSize.value(); 0362 Q_EMIT q->maximumSizeChanged(maximumSize); 0363 } 0364 0365 if (!xdgSurfacePrivate->isInitialized) { 0366 Q_EMIT q->initializeRequested(); 0367 xdgSurfacePrivate->isInitialized = true; 0368 } 0369 } 0370 0371 void XdgToplevelInterfacePrivate::reset() 0372 { 0373 auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface); 0374 xdgSurfacePrivate->reset(); 0375 0376 windowTitle = QString(); 0377 windowClass = QString(); 0378 minimumSize = QSize(); 0379 maximumSize = QSize(); 0380 pending = XdgToplevelCommit{}; 0381 stashed.clear(); 0382 0383 Q_EMIT q->resetOccurred(); 0384 } 0385 0386 void XdgToplevelInterfacePrivate::xdg_toplevel_destroy_resource(Resource *resource) 0387 { 0388 delete q; 0389 } 0390 0391 void XdgToplevelInterfacePrivate::xdg_toplevel_destroy(Resource *resource) 0392 { 0393 wl_resource_destroy(resource->handle); 0394 } 0395 0396 void XdgToplevelInterfacePrivate::xdg_toplevel_set_parent(Resource *resource, ::wl_resource *parentResource) 0397 { 0398 XdgToplevelInterface *parent = XdgToplevelInterface::get(parentResource); 0399 if (parentXdgToplevel == parent) { 0400 return; 0401 } 0402 parentXdgToplevel = parent; 0403 Q_EMIT q->parentXdgToplevelChanged(); 0404 } 0405 0406 void XdgToplevelInterfacePrivate::xdg_toplevel_set_title(Resource *resource, const QString &title) 0407 { 0408 if (windowTitle == title) { 0409 return; 0410 } 0411 windowTitle = title; 0412 Q_EMIT q->windowTitleChanged(title); 0413 } 0414 0415 void XdgToplevelInterfacePrivate::xdg_toplevel_set_app_id(Resource *resource, const QString &app_id) 0416 { 0417 if (windowClass == app_id) { 0418 return; 0419 } 0420 windowClass = app_id; 0421 Q_EMIT q->windowClassChanged(app_id); 0422 } 0423 0424 void XdgToplevelInterfacePrivate::xdg_toplevel_show_window_menu(Resource *resource, ::wl_resource *seatResource, uint32_t serial, int32_t x, int32_t y) 0425 { 0426 auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface); 0427 0428 if (!xdgSurfacePrivate->isConfigured) { 0429 wl_resource_post_error(resource->handle, QtWaylandServer::xdg_surface::error_not_constructed, "surface has not been configured yet"); 0430 return; 0431 } 0432 0433 SeatInterface *seat = SeatInterface::get(seatResource); 0434 Q_EMIT q->windowMenuRequested(seat, QPoint(x, y), serial); 0435 } 0436 0437 void XdgToplevelInterfacePrivate::xdg_toplevel_move(Resource *resource, ::wl_resource *seatResource, uint32_t serial) 0438 { 0439 auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface); 0440 0441 if (!xdgSurfacePrivate->isConfigured) { 0442 wl_resource_post_error(resource->handle, QtWaylandServer::xdg_surface::error_not_constructed, "surface has not been configured yet"); 0443 return; 0444 } 0445 0446 SeatInterface *seat = SeatInterface::get(seatResource); 0447 Q_EMIT q->moveRequested(seat, serial); 0448 } 0449 0450 void XdgToplevelInterfacePrivate::xdg_toplevel_resize(Resource *resource, ::wl_resource *seatResource, uint32_t serial, uint32_t xdgEdges) 0451 { 0452 auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface); 0453 0454 if (!xdgSurfacePrivate->isConfigured) { 0455 wl_resource_post_error(resource->handle, QtWaylandServer::xdg_surface::error_not_constructed, "surface has not been configured yet"); 0456 return; 0457 } 0458 0459 SeatInterface *seat = SeatInterface::get(seatResource); 0460 Q_EMIT q->resizeRequested(seat, XdgToplevelInterface::ResizeAnchor(xdgEdges), serial); 0461 } 0462 0463 void XdgToplevelInterfacePrivate::xdg_toplevel_set_max_size(Resource *resource, int32_t width, int32_t height) 0464 { 0465 if (width < 0 || height < 0) { 0466 wl_resource_post_error(resource->handle, error_invalid_size, "width and height must be positive or zero"); 0467 return; 0468 } 0469 pending.maximumSize = QSize(width, height); 0470 } 0471 0472 void XdgToplevelInterfacePrivate::xdg_toplevel_set_min_size(Resource *resource, int32_t width, int32_t height) 0473 { 0474 if (width < 0 || height < 0) { 0475 wl_resource_post_error(resource->handle, error_invalid_size, "width and height must be positive or zero"); 0476 return; 0477 } 0478 pending.minimumSize = QSize(width, height); 0479 } 0480 0481 void XdgToplevelInterfacePrivate::xdg_toplevel_set_maximized(Resource *resource) 0482 { 0483 Q_EMIT q->maximizeRequested(); 0484 } 0485 0486 void XdgToplevelInterfacePrivate::xdg_toplevel_unset_maximized(Resource *resource) 0487 { 0488 Q_EMIT q->unmaximizeRequested(); 0489 } 0490 0491 void XdgToplevelInterfacePrivate::xdg_toplevel_set_fullscreen(Resource *resource, ::wl_resource *outputResource) 0492 { 0493 OutputInterface *output = OutputInterface::get(outputResource); 0494 Q_EMIT q->fullscreenRequested(output); 0495 } 0496 0497 void XdgToplevelInterfacePrivate::xdg_toplevel_unset_fullscreen(Resource *resource) 0498 { 0499 Q_EMIT q->unfullscreenRequested(); 0500 } 0501 0502 void XdgToplevelInterfacePrivate::xdg_toplevel_set_minimized(Resource *resource) 0503 { 0504 Q_EMIT q->minimizeRequested(); 0505 } 0506 0507 XdgToplevelInterfacePrivate *XdgToplevelInterfacePrivate::get(XdgToplevelInterface *toplevel) 0508 { 0509 return toplevel->d.get(); 0510 } 0511 0512 XdgToplevelInterfacePrivate *XdgToplevelInterfacePrivate::get(wl_resource *resource) 0513 { 0514 return resource_cast<XdgToplevelInterfacePrivate *>(resource); 0515 } 0516 0517 XdgToplevelInterface::XdgToplevelInterface(XdgSurfaceInterface *xdgSurface, ::wl_resource *resource) 0518 : d(new XdgToplevelInterfacePrivate(this, xdgSurface)) 0519 { 0520 XdgSurfaceInterfacePrivate *surfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface); 0521 surfacePrivate->toplevel = this; 0522 surfacePrivate->pending = &d->pending; 0523 0524 d->init(resource); 0525 } 0526 0527 XdgToplevelInterface::~XdgToplevelInterface() 0528 { 0529 Q_EMIT aboutToBeDestroyed(); 0530 0531 XdgSurfaceInterfacePrivate *surfacePrivate = XdgSurfaceInterfacePrivate::get(d->xdgSurface); 0532 surfacePrivate->toplevel = nullptr; 0533 surfacePrivate->pending = nullptr; 0534 } 0535 0536 SurfaceRole *XdgToplevelInterface::role() 0537 { 0538 static SurfaceRole role(QByteArrayLiteral("xdg_toplevel")); 0539 return &role; 0540 } 0541 0542 XdgShellInterface *XdgToplevelInterface::shell() const 0543 { 0544 return d->xdgSurface->shell(); 0545 } 0546 0547 XdgSurfaceInterface *XdgToplevelInterface::xdgSurface() const 0548 { 0549 return d->xdgSurface; 0550 } 0551 0552 SurfaceInterface *XdgToplevelInterface::surface() const 0553 { 0554 return d->xdgSurface->surface(); 0555 } 0556 0557 bool XdgToplevelInterface::isConfigured() const 0558 { 0559 return d->xdgSurface->isConfigured(); 0560 } 0561 0562 XdgToplevelInterface *XdgToplevelInterface::parentXdgToplevel() const 0563 { 0564 return d->parentXdgToplevel; 0565 } 0566 0567 QString XdgToplevelInterface::windowTitle() const 0568 { 0569 return d->windowTitle; 0570 } 0571 0572 QString XdgToplevelInterface::windowClass() const 0573 { 0574 return d->windowClass; 0575 } 0576 0577 QSize XdgToplevelInterface::minimumSize() const 0578 { 0579 return d->minimumSize.isEmpty() ? QSize(0, 0) : d->minimumSize; 0580 } 0581 0582 QSize XdgToplevelInterface::maximumSize() const 0583 { 0584 return d->maximumSize.isEmpty() ? QSize(INT_MAX, INT_MAX) : d->maximumSize; 0585 } 0586 0587 quint32 XdgToplevelInterface::sendConfigure(const QSize &size, const States &states) 0588 { 0589 // Note that the states listed in the configure event must be an array of uint32_t. 0590 0591 uint32_t statesData[9] = {0}; 0592 int i = 0; 0593 0594 if (states & State::MaximizedHorizontal && states & State::MaximizedVertical) { 0595 statesData[i++] = QtWaylandServer::xdg_toplevel::state_maximized; 0596 } 0597 if (states & State::FullScreen) { 0598 statesData[i++] = QtWaylandServer::xdg_toplevel::state_fullscreen; 0599 } 0600 if (states & State::Resizing) { 0601 statesData[i++] = QtWaylandServer::xdg_toplevel::state_resizing; 0602 } 0603 if (states & State::Activated) { 0604 statesData[i++] = QtWaylandServer::xdg_toplevel::state_activated; 0605 } 0606 0607 if (d->resource()->version() >= XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION) { 0608 if (states & State::TiledLeft) { 0609 statesData[i++] = QtWaylandServer::xdg_toplevel::state_tiled_left; 0610 } 0611 if (states & State::TiledTop) { 0612 statesData[i++] = QtWaylandServer::xdg_toplevel::state_tiled_top; 0613 } 0614 if (states & State::TiledRight) { 0615 statesData[i++] = QtWaylandServer::xdg_toplevel::state_tiled_right; 0616 } 0617 if (states & State::TiledBottom) { 0618 statesData[i++] = QtWaylandServer::xdg_toplevel::state_tiled_bottom; 0619 } 0620 } 0621 0622 if (d->resource()->version() >= XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION) { 0623 if (states & State::Suspended) { 0624 statesData[i++] = QtWaylandServer::xdg_toplevel::state_suspended; 0625 } 0626 } 0627 0628 const QByteArray xdgStates = QByteArray::fromRawData(reinterpret_cast<char *>(statesData), sizeof(uint32_t) * i); 0629 const quint32 serial = xdgSurface()->shell()->display()->nextSerial(); 0630 0631 d->send_configure(size.width(), size.height(), xdgStates); 0632 0633 auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface()); 0634 xdgSurfacePrivate->send_configure(serial); 0635 xdgSurfacePrivate->isConfigured = true; 0636 0637 return serial; 0638 } 0639 0640 void XdgToplevelInterface::sendClose() 0641 { 0642 d->send_close(); 0643 } 0644 0645 void XdgToplevelInterface::sendConfigureBounds(const QSize &size) 0646 { 0647 if (d->resource()->version() >= XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION) { 0648 d->send_configure_bounds(size.width(), size.height()); 0649 } 0650 } 0651 0652 void XdgToplevelInterface::sendWmCapabilities(Capabilities capabilities) 0653 { 0654 if (d->resource()->version() < XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION) { 0655 return; 0656 } 0657 // Note that the capabilities listed in the event must be an array of uint32_t. 0658 0659 uint32_t capabilitiesData[4] = {0}; 0660 int i = 0; 0661 0662 if (capabilities & Capability::WindowMenu) { 0663 capabilitiesData[i++] = QtWaylandServer::xdg_toplevel::wm_capabilities_window_menu; 0664 } 0665 if (capabilities & Capability::Maximize) { 0666 capabilitiesData[i++] = QtWaylandServer::xdg_toplevel::wm_capabilities_maximize; 0667 } 0668 if (capabilities & Capability::FullScreen) { 0669 capabilitiesData[i++] = QtWaylandServer::xdg_toplevel::wm_capabilities_fullscreen; 0670 } 0671 if (capabilities & Capability::Minimize) { 0672 capabilitiesData[i++] = QtWaylandServer::xdg_toplevel::wm_capabilities_minimize; 0673 } 0674 0675 d->send_wm_capabilities(QByteArray::fromRawData(reinterpret_cast<char *>(capabilitiesData), sizeof(uint32_t) * i)); 0676 } 0677 0678 XdgToplevelInterface *XdgToplevelInterface::get(::wl_resource *resource) 0679 { 0680 if (auto toplevelPrivate = resource_cast<XdgToplevelInterfacePrivate *>(resource)) { 0681 return toplevelPrivate->q; 0682 } 0683 return nullptr; 0684 } 0685 0686 XdgPopupInterfacePrivate *XdgPopupInterfacePrivate::get(XdgPopupInterface *popup) 0687 { 0688 return popup->d.get(); 0689 } 0690 0691 XdgPopupInterfacePrivate::XdgPopupInterfacePrivate(XdgPopupInterface *popup, XdgSurfaceInterface *xdgSurface) 0692 : SurfaceExtension(xdgSurface->surface()) 0693 , q(popup) 0694 , xdgSurface(xdgSurface) 0695 { 0696 } 0697 0698 void XdgPopupInterfacePrivate::apply(XdgPopupCommit *commit) 0699 { 0700 if (!parentSurface) { 0701 auto shellPrivate = XdgShellInterfacePrivate::get(xdgSurface->shell()); 0702 wl_resource_post_error(shellPrivate->resourceForXdgSurface(xdgSurface)->handle, 0703 QtWaylandServer::xdg_wm_base::error_invalid_popup_parent, 0704 "no xdg_popup parent surface has been specified"); 0705 return; 0706 } 0707 0708 auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface); 0709 if (xdgSurfacePrivate->firstBufferAttached && !xdgSurfacePrivate->surface->buffer()) { 0710 reset(); 0711 return; 0712 } 0713 0714 xdgSurfacePrivate->apply(commit); 0715 0716 if (!xdgSurfacePrivate->isInitialized) { 0717 Q_EMIT q->initializeRequested(); 0718 xdgSurfacePrivate->isInitialized = true; 0719 } 0720 } 0721 0722 void XdgPopupInterfacePrivate::reset() 0723 { 0724 auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface); 0725 pending = XdgPopupCommit{}; 0726 stashed.clear(); 0727 xdgSurfacePrivate->reset(); 0728 } 0729 0730 void XdgPopupInterfacePrivate::xdg_popup_destroy_resource(Resource *resource) 0731 { 0732 delete q; 0733 } 0734 0735 void XdgPopupInterfacePrivate::xdg_popup_destroy(Resource *resource) 0736 { 0737 // TODO: We need to post an error with the code XDG_WM_BASE_ERROR_NOT_THE_TOPMOST_POPUP if 0738 // this popup is not the topmost grabbing popup. We most likely need a grab abstraction or 0739 // something to determine whether the given popup has an explicit grab. 0740 wl_resource_destroy(resource->handle); 0741 } 0742 0743 void XdgPopupInterfacePrivate::xdg_popup_grab(Resource *resource, ::wl_resource *seatHandle, uint32_t serial) 0744 { 0745 if (xdgSurface->surface()->buffer()) { 0746 wl_resource_post_error(resource->handle, error_invalid_grab, "xdg_surface is already mapped"); 0747 return; 0748 } 0749 SeatInterface *seat = SeatInterface::get(seatHandle); 0750 Q_EMIT q->grabRequested(seat, serial); 0751 } 0752 0753 void XdgPopupInterfacePrivate::xdg_popup_reposition(Resource *resource, ::wl_resource *positionerResource, uint32_t token) 0754 { 0755 positioner = XdgPositioner::get(positionerResource); 0756 Q_EMIT q->repositionRequested(token); 0757 } 0758 0759 XdgPopupInterface::XdgPopupInterface(XdgSurfaceInterface *xdgSurface, SurfaceInterface *parentSurface, const XdgPositioner &positioner, ::wl_resource *resource) 0760 : d(new XdgPopupInterfacePrivate(this, xdgSurface)) 0761 { 0762 XdgSurfaceInterfacePrivate *surfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface); 0763 surfacePrivate->popup = this; 0764 surfacePrivate->pending = &d->pending; 0765 0766 d->parentSurface = parentSurface; 0767 d->positioner = positioner; 0768 d->init(resource); 0769 } 0770 0771 XdgPopupInterface::~XdgPopupInterface() 0772 { 0773 Q_EMIT aboutToBeDestroyed(); 0774 0775 XdgSurfaceInterfacePrivate *surfacePrivate = XdgSurfaceInterfacePrivate::get(d->xdgSurface); 0776 surfacePrivate->popup = nullptr; 0777 surfacePrivate->pending = nullptr; 0778 } 0779 0780 SurfaceRole *XdgPopupInterface::role() 0781 { 0782 static SurfaceRole role(QByteArrayLiteral("xdg_popup")); 0783 return &role; 0784 } 0785 0786 SurfaceInterface *XdgPopupInterface::parentSurface() const 0787 { 0788 return d->parentSurface; 0789 } 0790 0791 XdgSurfaceInterface *XdgPopupInterface::xdgSurface() const 0792 { 0793 return d->xdgSurface; 0794 } 0795 0796 SurfaceInterface *XdgPopupInterface::surface() const 0797 { 0798 return d->xdgSurface->surface(); 0799 } 0800 0801 bool XdgPopupInterface::isConfigured() const 0802 { 0803 return d->xdgSurface->isConfigured(); 0804 } 0805 0806 XdgPositioner XdgPopupInterface::positioner() const 0807 { 0808 return d->positioner; 0809 } 0810 0811 quint32 XdgPopupInterface::sendConfigure(const QRect &rect) 0812 { 0813 const quint32 serial = xdgSurface()->shell()->display()->nextSerial(); 0814 0815 d->send_configure(rect.x(), rect.y(), rect.width(), rect.height()); 0816 0817 auto xdgSurfacePrivate = XdgSurfaceInterfacePrivate::get(xdgSurface()); 0818 xdgSurfacePrivate->send_configure(serial); 0819 xdgSurfacePrivate->isConfigured = true; 0820 0821 return serial; 0822 } 0823 0824 void XdgPopupInterface::sendPopupDone() 0825 { 0826 d->send_popup_done(); 0827 } 0828 0829 void XdgPopupInterface::sendRepositioned(quint32 token) 0830 { 0831 Q_ASSERT(d->resource()->version() >= XDG_POPUP_REPOSITIONED_SINCE_VERSION); 0832 d->send_repositioned(token); 0833 } 0834 0835 XdgPopupInterface *XdgPopupInterface::get(::wl_resource *resource) 0836 { 0837 if (auto popupPrivate = resource_cast<XdgPopupInterfacePrivate *>(resource)) { 0838 return popupPrivate->q; 0839 } 0840 return nullptr; 0841 } 0842 0843 XdgPositionerPrivate::XdgPositionerPrivate(::wl_resource *resource) 0844 : data(new XdgPositionerData) 0845 { 0846 init(resource); 0847 } 0848 0849 XdgPositionerPrivate *XdgPositionerPrivate::get(wl_resource *resource) 0850 { 0851 return resource_cast<XdgPositionerPrivate *>(resource); 0852 } 0853 0854 void XdgPositionerPrivate::xdg_positioner_destroy_resource(Resource *resource) 0855 { 0856 delete this; 0857 } 0858 0859 void XdgPositionerPrivate::xdg_positioner_destroy(Resource *resource) 0860 { 0861 wl_resource_destroy(resource->handle); 0862 } 0863 0864 void XdgPositionerPrivate::xdg_positioner_set_size(Resource *resource, int32_t width, int32_t height) 0865 { 0866 if (width < 1 || height < 1) { 0867 wl_resource_post_error(resource->handle, error_invalid_input, "width and height must be positive and non-zero"); 0868 return; 0869 } 0870 data->size = QSize(width, height); 0871 } 0872 0873 void XdgPositionerPrivate::xdg_positioner_set_anchor_rect(Resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) 0874 { 0875 if (width < 1 || height < 1) { 0876 wl_resource_post_error(resource->handle, error_invalid_input, "width and height must be positive and non-zero"); 0877 return; 0878 } 0879 data->anchorRect = QRect(x, y, width, height); 0880 } 0881 0882 void XdgPositionerPrivate::xdg_positioner_set_anchor(Resource *resource, uint32_t anchor) 0883 { 0884 if (anchor > anchor_bottom_right) { 0885 wl_resource_post_error(resource->handle, error_invalid_input, "unknown anchor point"); 0886 return; 0887 } 0888 0889 switch (anchor) { 0890 case anchor_top: 0891 data->anchorEdges = Qt::TopEdge; 0892 break; 0893 case anchor_top_right: 0894 data->anchorEdges = Qt::TopEdge | Qt::RightEdge; 0895 break; 0896 case anchor_right: 0897 data->anchorEdges = Qt::RightEdge; 0898 break; 0899 case anchor_bottom_right: 0900 data->anchorEdges = Qt::BottomEdge | Qt::RightEdge; 0901 break; 0902 case anchor_bottom: 0903 data->anchorEdges = Qt::BottomEdge; 0904 break; 0905 case anchor_bottom_left: 0906 data->anchorEdges = Qt::BottomEdge | Qt::LeftEdge; 0907 break; 0908 case anchor_left: 0909 data->anchorEdges = Qt::LeftEdge; 0910 break; 0911 case anchor_top_left: 0912 data->anchorEdges = Qt::TopEdge | Qt::LeftEdge; 0913 break; 0914 default: 0915 data->anchorEdges = Qt::Edges(); 0916 break; 0917 } 0918 } 0919 0920 void XdgPositionerPrivate::xdg_positioner_set_parent_size(Resource *resource, int32_t width, int32_t height) 0921 { 0922 data->parentSize = QSize(width, height); 0923 } 0924 0925 void XdgPositionerPrivate::xdg_positioner_set_reactive(Resource *resource) 0926 { 0927 data->isReactive = true; 0928 } 0929 0930 void XdgPositionerPrivate::xdg_positioner_set_parent_configure(Resource *resource, uint32_t serial) 0931 { 0932 data->parentConfigure = serial; 0933 } 0934 0935 void XdgPositionerPrivate::xdg_positioner_set_gravity(Resource *resource, uint32_t gravity) 0936 { 0937 if (gravity > gravity_bottom_right) { 0938 wl_resource_post_error(resource->handle, error_invalid_input, "unknown gravity direction"); 0939 return; 0940 } 0941 0942 switch (gravity) { 0943 case gravity_top: 0944 data->gravityEdges = Qt::TopEdge; 0945 break; 0946 case gravity_top_right: 0947 data->gravityEdges = Qt::TopEdge | Qt::RightEdge; 0948 break; 0949 case gravity_right: 0950 data->gravityEdges = Qt::RightEdge; 0951 break; 0952 case gravity_bottom_right: 0953 data->gravityEdges = Qt::BottomEdge | Qt::RightEdge; 0954 break; 0955 case gravity_bottom: 0956 data->gravityEdges = Qt::BottomEdge; 0957 break; 0958 case gravity_bottom_left: 0959 data->gravityEdges = Qt::BottomEdge | Qt::LeftEdge; 0960 break; 0961 case gravity_left: 0962 data->gravityEdges = Qt::LeftEdge; 0963 break; 0964 case gravity_top_left: 0965 data->gravityEdges = Qt::TopEdge | Qt::LeftEdge; 0966 break; 0967 default: 0968 data->gravityEdges = Qt::Edges(); 0969 break; 0970 } 0971 } 0972 0973 void XdgPositionerPrivate::xdg_positioner_set_constraint_adjustment(Resource *resource, uint32_t constraint_adjustment) 0974 { 0975 if (constraint_adjustment & constraint_adjustment_flip_x) { 0976 data->flipConstraintAdjustments |= Qt::Horizontal; 0977 } else { 0978 data->flipConstraintAdjustments &= ~Qt::Horizontal; 0979 } 0980 0981 if (constraint_adjustment & constraint_adjustment_flip_y) { 0982 data->flipConstraintAdjustments |= Qt::Vertical; 0983 } else { 0984 data->flipConstraintAdjustments &= ~Qt::Vertical; 0985 } 0986 0987 if (constraint_adjustment & constraint_adjustment_slide_x) { 0988 data->slideConstraintAdjustments |= Qt::Horizontal; 0989 } else { 0990 data->slideConstraintAdjustments &= ~Qt::Horizontal; 0991 } 0992 0993 if (constraint_adjustment & constraint_adjustment_slide_y) { 0994 data->slideConstraintAdjustments |= Qt::Vertical; 0995 } else { 0996 data->slideConstraintAdjustments &= ~Qt::Vertical; 0997 } 0998 0999 if (constraint_adjustment & constraint_adjustment_resize_x) { 1000 data->resizeConstraintAdjustments |= Qt::Horizontal; 1001 } else { 1002 data->resizeConstraintAdjustments &= ~Qt::Horizontal; 1003 } 1004 1005 if (constraint_adjustment & constraint_adjustment_resize_y) { 1006 data->resizeConstraintAdjustments |= Qt::Vertical; 1007 } else { 1008 data->resizeConstraintAdjustments &= ~Qt::Vertical; 1009 } 1010 } 1011 1012 void XdgPositionerPrivate::xdg_positioner_set_offset(Resource *resource, int32_t x, int32_t y) 1013 { 1014 data->offset = QPoint(x, y); 1015 } 1016 1017 XdgPositioner::XdgPositioner() 1018 : d(new XdgPositionerData) 1019 { 1020 } 1021 1022 XdgPositioner::XdgPositioner(const XdgPositioner &other) 1023 : d(other.d) 1024 { 1025 } 1026 1027 XdgPositioner::~XdgPositioner() 1028 { 1029 } 1030 1031 XdgPositioner &XdgPositioner::operator=(const XdgPositioner &other) 1032 { 1033 d = other.d; 1034 return *this; 1035 } 1036 1037 bool XdgPositioner::isComplete() const 1038 { 1039 return d->size.isValid() && d->anchorRect.isValid(); 1040 } 1041 1042 QSize XdgPositioner::size() const 1043 { 1044 return d->size; 1045 } 1046 1047 bool XdgPositioner::isReactive() const 1048 { 1049 return d->isReactive; 1050 } 1051 1052 QRectF XdgPositioner::placement(const QRectF &bounds) const 1053 { 1054 // returns if a target is within the supplied bounds, optional edges argument states which side to check 1055 auto inBounds = [bounds](const QRectF &target, Qt::Edges edges = Qt::LeftEdge | Qt::RightEdge | Qt::TopEdge | Qt::BottomEdge) -> bool { 1056 if (edges & Qt::LeftEdge && target.left() < bounds.left()) { 1057 return false; 1058 } 1059 if (edges & Qt::TopEdge && target.top() < bounds.top()) { 1060 return false; 1061 } 1062 if (edges & Qt::RightEdge && target.right() > bounds.right()) { 1063 // normal QRect::right issue cancels out 1064 return false; 1065 } 1066 if (edges & Qt::BottomEdge && target.bottom() > bounds.bottom()) { 1067 return false; 1068 } 1069 return true; 1070 }; 1071 1072 QRectF popupRect(popupOffset(d->anchorRect, d->anchorEdges, d->gravityEdges, d->size) + d->offset, d->size); 1073 1074 // if that fits, we don't need to do anything 1075 if (inBounds(popupRect)) { 1076 return popupRect; 1077 } 1078 // otherwise apply constraint adjustment per axis in order XDG Shell Popup states 1079 1080 if (d->flipConstraintAdjustments & Qt::Horizontal) { 1081 if (!inBounds(popupRect, Qt::LeftEdge | Qt::RightEdge)) { 1082 // flip both edges (if either bit is set, XOR both) 1083 auto flippedAnchorEdge = d->anchorEdges; 1084 if (flippedAnchorEdge & (Qt::LeftEdge | Qt::RightEdge)) { 1085 flippedAnchorEdge ^= (Qt::LeftEdge | Qt::RightEdge); 1086 } 1087 auto flippedGravity = d->gravityEdges; 1088 if (flippedGravity & (Qt::LeftEdge | Qt::RightEdge)) { 1089 flippedGravity ^= (Qt::LeftEdge | Qt::RightEdge); 1090 } 1091 auto flippedPopupRect = QRectF(popupOffset(d->anchorRect, flippedAnchorEdge, flippedGravity, d->size) + d->offset, d->size); 1092 1093 // if it still doesn't fit we should continue with the unflipped version 1094 if (inBounds(flippedPopupRect, Qt::LeftEdge | Qt::RightEdge)) { 1095 popupRect.moveLeft(flippedPopupRect.left()); 1096 } 1097 } 1098 } 1099 if (d->slideConstraintAdjustments & Qt::Horizontal) { 1100 if (!inBounds(popupRect, Qt::LeftEdge)) { 1101 popupRect.moveLeft(bounds.left()); 1102 } 1103 if (!inBounds(popupRect, Qt::RightEdge)) { 1104 popupRect.moveRight(bounds.right()); 1105 } 1106 } 1107 if (d->resizeConstraintAdjustments & Qt::Horizontal) { 1108 QRectF unconstrainedRect = popupRect; 1109 1110 if (!inBounds(unconstrainedRect, Qt::LeftEdge)) { 1111 unconstrainedRect.setLeft(bounds.left()); 1112 } 1113 if (!inBounds(unconstrainedRect, Qt::RightEdge)) { 1114 unconstrainedRect.setRight(bounds.right()); 1115 } 1116 1117 if (unconstrainedRect.isValid()) { 1118 popupRect = unconstrainedRect; 1119 } 1120 } 1121 1122 if (d->flipConstraintAdjustments & Qt::Vertical) { 1123 if (!inBounds(popupRect, Qt::TopEdge | Qt::BottomEdge)) { 1124 // flip both edges (if either bit is set, XOR both) 1125 auto flippedAnchorEdge = d->anchorEdges; 1126 if (flippedAnchorEdge & (Qt::TopEdge | Qt::BottomEdge)) { 1127 flippedAnchorEdge ^= (Qt::TopEdge | Qt::BottomEdge); 1128 } 1129 auto flippedGravity = d->gravityEdges; 1130 if (flippedGravity & (Qt::TopEdge | Qt::BottomEdge)) { 1131 flippedGravity ^= (Qt::TopEdge | Qt::BottomEdge); 1132 } 1133 auto flippedPopupRect = QRectF(popupOffset(d->anchorRect, flippedAnchorEdge, flippedGravity, d->size) + d->offset, d->size); 1134 1135 // if it still doesn't fit we should continue with the unflipped version 1136 if (inBounds(flippedPopupRect, Qt::TopEdge | Qt::BottomEdge)) { 1137 popupRect.moveTop(flippedPopupRect.top()); 1138 } 1139 } 1140 } 1141 if (d->slideConstraintAdjustments & Qt::Vertical) { 1142 if (!inBounds(popupRect, Qt::TopEdge)) { 1143 popupRect.moveTop(bounds.top()); 1144 } 1145 if (!inBounds(popupRect, Qt::BottomEdge)) { 1146 popupRect.moveBottom(bounds.bottom()); 1147 } 1148 } 1149 if (d->resizeConstraintAdjustments & Qt::Vertical) { 1150 QRectF unconstrainedRect = popupRect; 1151 1152 if (!inBounds(unconstrainedRect, Qt::TopEdge)) { 1153 unconstrainedRect.setTop(bounds.top()); 1154 } 1155 if (!inBounds(unconstrainedRect, Qt::BottomEdge)) { 1156 unconstrainedRect.setBottom(bounds.bottom()); 1157 } 1158 1159 if (unconstrainedRect.isValid()) { 1160 popupRect = unconstrainedRect; 1161 } 1162 } 1163 1164 return popupRect; 1165 } 1166 1167 XdgPositioner XdgPositioner::get(::wl_resource *resource) 1168 { 1169 XdgPositionerPrivate *xdgPositionerPrivate = XdgPositionerPrivate::get(resource); 1170 if (xdgPositionerPrivate) 1171 return XdgPositioner(xdgPositionerPrivate->data); 1172 return XdgPositioner(); 1173 } 1174 1175 XdgPositioner::XdgPositioner(const QSharedDataPointer<XdgPositionerData> &data) 1176 : d(data) 1177 { 1178 } 1179 1180 } // namespace KWin 1181 1182 #include "moc_xdgshell.cpp"