File indexing completed on 2024-04-28 09:26:02

0001 /*
0002  *   SPDX-FileCopyrightText: 2021 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
0003  *   SPDX-FileCopyrightText: 2018 Drew DeVault <sir@cmpwn.com>
0004  *
0005  *   SPDX-License-Identifier: LGPL-3.0-or-later
0006  */
0007 
0008 #include "interfaces/window.h"
0009 #include "layershellqt_logging.h"
0010 #include "qwaylandlayersurface_p.h"
0011 #include "qwaylandxdgactivationv1_p.h"
0012 
0013 #include <QtWaylandClient/private/qwaylandscreen_p.h>
0014 #include <QtWaylandClient/private/qwaylandsurface_p.h>
0015 #include <QtWaylandClient/private/qwaylandwindow_p.h>
0016 
0017 #include <QGuiApplication>
0018 
0019 namespace LayerShellQt
0020 {
0021 QWaylandLayerSurface::QWaylandLayerSurface(QWaylandLayerShellIntegration *shell, QtWaylandClient::QWaylandWindow *window)
0022     : QtWaylandClient::QWaylandShellSurface(window)
0023     , QtWayland::zwlr_layer_surface_v1()
0024     , m_shell(shell)
0025     , m_interface(Window::get(window->window()))
0026 {
0027     wl_output *output = nullptr;
0028     if (m_interface->screenConfiguration() == Window::ScreenFromQWindow) {
0029         auto waylandScreen = dynamic_cast<QtWaylandClient::QWaylandScreen *>(window->window()->screen()->handle());
0030         // Qt will always assign a screen to a window, but if the compositor has no screens available a dummy QScreen object is created
0031         // this will not cast to a QWaylandScreen
0032         if (!waylandScreen) {
0033             qCWarning(LAYERSHELLQT) << "Creating a layer shell for placeholder screen. This will be positioned incorrectly";
0034         } else {
0035             output = waylandScreen->output();
0036         }
0037     }
0038     init(shell->get_layer_surface(window->waylandSurface()->object(), output, m_interface->layer(), m_interface->scope()));
0039     connect(m_interface, &Window::layerChanged, this, [this]() {
0040         setLayer(m_interface->layer());
0041     });
0042 
0043     set_anchor(m_interface->anchors());
0044     connect(m_interface, &Window::anchorsChanged, this, [this]() {
0045         set_anchor(m_interface->anchors());
0046     });
0047     setExclusiveZone(m_interface->exclusionZone());
0048     connect(m_interface, &Window::exclusionZoneChanged, this, [this]() {
0049         setExclusiveZone(m_interface->exclusionZone());
0050     });
0051     setExclusiveEdge(m_interface->exclusiveEdge());
0052     connect(m_interface, &Window::exclusiveEdgeChanged, this, [this]() {
0053         setExclusiveEdge(m_interface->exclusiveEdge());
0054     });
0055 
0056     setMargins(m_interface->margins());
0057     connect(m_interface, &Window::marginsChanged, this, [this]() {
0058         setMargins(m_interface->margins());
0059     });
0060 
0061     setKeyboardInteractivity(m_interface->keyboardInteractivity());
0062     connect(m_interface, &Window::keyboardInteractivityChanged, this, [this]() {
0063         setKeyboardInteractivity(m_interface->keyboardInteractivity());
0064     });
0065 
0066     QSize size = window->surfaceSize();
0067     const Window::Anchors anchors = m_interface->anchors();
0068     if ((anchors & Window::AnchorLeft) && (anchors & Window::AnchorRight)) {
0069         size.setWidth(0);
0070     }
0071     if ((anchors & Window::AnchorTop) && (anchors & Window::AnchorBottom)) {
0072         size.setHeight(0);
0073     }
0074     if (size.isValid() && size != QSize(0, 0)) {
0075         set_size(size.width(), size.height());
0076     }
0077 }
0078 
0079 QWaylandLayerSurface::~QWaylandLayerSurface()
0080 {
0081     destroy();
0082 }
0083 
0084 void QWaylandLayerSurface::zwlr_layer_surface_v1_closed()
0085 {
0086     if (m_interface->closeOnDismissed()) {
0087         window()->window()->close();
0088     }
0089 }
0090 
0091 void QWaylandLayerSurface::zwlr_layer_surface_v1_configure(uint32_t serial, uint32_t width, uint32_t height)
0092 {
0093     ack_configure(serial);
0094     m_pendingSize = QSize(width, height);
0095 
0096     if (!m_configured) {
0097         m_configured = true;
0098         window()->resizeFromApplyConfigure(m_pendingSize);
0099 #if QT_VERSION < QT_VERSION_CHECK(6, 7, 0)
0100         window()->handleExpose(QRect(QPoint(), m_pendingSize));
0101 #else
0102         window()->sendRecursiveExposeEvent();
0103 #endif
0104     } else {
0105         // Later configures are resizes, so we have to queue them up for a time when we
0106         // are not painting to the window.
0107         window()->applyConfigureWhenPossible();
0108     }
0109 }
0110 
0111 void QWaylandLayerSurface::attachPopup(QtWaylandClient::QWaylandShellSurface *popup)
0112 {
0113     std::any anyRole = popup->surfaceRole();
0114 
0115     if (auto role = std::any_cast<::xdg_popup *>(&anyRole)) {
0116         get_popup(*role);
0117     } else {
0118         qCWarning(LAYERSHELLQT) << "Cannot attach popup of unknown type";
0119     }
0120 }
0121 
0122 void QWaylandLayerSurface::applyConfigure()
0123 {
0124     window()->resizeFromApplyConfigure(m_pendingSize);
0125 }
0126 
0127 void QWaylandLayerSurface::setAnchor(uint anchor)
0128 {
0129     set_anchor(anchor);
0130     setWindowGeometry(window()->windowContentGeometry());
0131 }
0132 
0133 void QWaylandLayerSurface::setExclusiveZone(int32_t zone)
0134 {
0135     set_exclusive_zone(zone);
0136 }
0137 
0138 void QWaylandLayerSurface::setExclusiveEdge(uint32_t edge)
0139 {
0140     if (zwlr_layer_surface_v1_get_version(object()) >= ZWLR_LAYER_SURFACE_V1_SET_EXCLUSIVE_EDGE_SINCE_VERSION) {
0141         set_exclusive_edge(edge);
0142     }
0143 }
0144 
0145 void QWaylandLayerSurface::setMargins(const QMargins &margins)
0146 {
0147     set_margin(margins.top(), margins.right(), margins.bottom(), margins.left());
0148 }
0149 
0150 void QWaylandLayerSurface::setKeyboardInteractivity(uint32_t interactivity)
0151 {
0152     set_keyboard_interactivity(interactivity);
0153 }
0154 
0155 void QWaylandLayerSurface::setLayer(uint32_t layer)
0156 {
0157     if (zwlr_layer_surface_v1_get_version(object()) >= ZWLR_LAYER_SURFACE_V1_SET_LAYER_SINCE_VERSION)
0158         set_layer(layer);
0159 }
0160 
0161 void QWaylandLayerSurface::setWindowGeometry(const QRect &geometry)
0162 {
0163     const bool horizontallyConstrained = m_interface->anchors().testFlags({Window::AnchorLeft, Window::AnchorRight});
0164     const bool verticallyConstrained = m_interface->anchors().testFlags({Window::AnchorTop, Window::AnchorBottom});
0165 
0166     QSize size = geometry.size();
0167     if (horizontallyConstrained) {
0168         size.setWidth(0);
0169     }
0170     if (verticallyConstrained) {
0171         size.setHeight(0);
0172     }
0173     set_size(size.width(), size.height());
0174 }
0175 
0176 bool QWaylandLayerSurface::requestActivate()
0177 {
0178     QWaylandXdgActivationV1 *activation = m_shell->activation();
0179     if (!activation->isActive()) {
0180         return false;
0181     }
0182     if (!m_activationToken.isEmpty()) {
0183         activation->activate(m_activationToken, window()->wlSurface());
0184         m_activationToken = {};
0185         return true;
0186     } else {
0187         const auto focusWindow = QGuiApplication::focusWindow();
0188         const auto wlWindow = focusWindow ? static_cast<QtWaylandClient::QWaylandWindow*>(focusWindow->handle()) : window();
0189         if (const auto seat = wlWindow->display()->lastInputDevice()) {
0190             const auto tokenProvider = activation->requestXdgActivationToken(
0191                 wlWindow->display(), wlWindow->wlSurface(), 0, QString());
0192             connect(tokenProvider, &QWaylandXdgActivationTokenV1::done, this,
0193                     [this](const QString &token) {
0194                         m_shell->activation()->activate(token, window()->wlSurface());
0195                     });
0196             connect(tokenProvider, &QWaylandXdgActivationTokenV1::done, tokenProvider, &QObject::deleteLater);
0197             return true;
0198         }
0199     }
0200     return false;
0201 }
0202 
0203 void QWaylandLayerSurface::setXdgActivationToken(const QString &token)
0204 {
0205     m_activationToken = token;
0206 }
0207 
0208 void QWaylandLayerSurface::requestXdgActivationToken(quint32 serial)
0209 {
0210     QWaylandXdgActivationV1 *activation = m_shell->activation();
0211     if (!activation->isActive()) {
0212         return;
0213     }
0214     auto tokenProvider = activation->requestXdgActivationToken(
0215         window()->display(), window()->wlSurface(), serial, QString());
0216 
0217     connect(tokenProvider, &QWaylandXdgActivationTokenV1::done, this,
0218             [this](const QString &token) {
0219                 Q_EMIT window()->xdgActivationTokenCreated(token);
0220             });
0221     connect(tokenProvider, &QWaylandXdgActivationTokenV1::done, tokenProvider, &QObject::deleteLater);
0222 
0223 }
0224 
0225 }
0226