File indexing completed on 2024-09-01 05:13:42
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