File indexing completed on 2024-04-28 05:30:23

0001 /*
0002     SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "layershellv1window.h"
0008 #include "core/output.h"
0009 #include "layershellv1integration.h"
0010 #include "screenedge.h"
0011 #include "wayland/layershell_v1.h"
0012 #include "wayland/output.h"
0013 #include "wayland/screenedge_v1.h"
0014 #include "wayland/surface.h"
0015 #include "wayland_server.h"
0016 #include "workspace.h"
0017 
0018 namespace KWin
0019 {
0020 
0021 static NET::WindowType scopeToType(const QString &scope)
0022 {
0023     static const QHash<QString, NET::WindowType> scopeToType{
0024         {QStringLiteral("desktop"), NET::Desktop},
0025         {QStringLiteral("dock"), NET::Dock},
0026         {QStringLiteral("crititical-notification"), NET::CriticalNotification},
0027         {QStringLiteral("notification"), NET::Notification},
0028         {QStringLiteral("tooltip"), NET::Tooltip},
0029         {QStringLiteral("on-screen-display"), NET::OnScreenDisplay},
0030         {QStringLiteral("dialog"), NET::Dialog},
0031         {QStringLiteral("splash"), NET::Splash},
0032         {QStringLiteral("utility"), NET::Utility},
0033     };
0034     return scopeToType.value(scope.toLower(), NET::Normal);
0035 }
0036 
0037 LayerShellV1Window::LayerShellV1Window(LayerSurfaceV1Interface *shellSurface,
0038                                        Output *output,
0039                                        LayerShellV1Integration *integration)
0040     : WaylandWindow(shellSurface->surface())
0041     , m_desiredOutput(output)
0042     , m_integration(integration)
0043     , m_shellSurface(shellSurface)
0044     , m_windowType(scopeToType(shellSurface->scope()))
0045 {
0046     setSkipSwitcher(!isDesktop());
0047     setSkipPager(true);
0048     setSkipTaskbar(true);
0049 
0050     connect(shellSurface, &LayerSurfaceV1Interface::aboutToBeDestroyed,
0051             this, &LayerShellV1Window::destroyWindow);
0052     connect(shellSurface->surface(), &SurfaceInterface::aboutToBeDestroyed,
0053             this, &LayerShellV1Window::destroyWindow);
0054 
0055     connect(output, &Output::geometryChanged,
0056             this, &LayerShellV1Window::scheduleRearrange);
0057     connect(output, &Output::enabledChanged,
0058             this, &LayerShellV1Window::handleOutputEnabledChanged);
0059 
0060     connect(shellSurface->surface(), &SurfaceInterface::sizeChanged,
0061             this, &LayerShellV1Window::handleSizeChanged);
0062     connect(shellSurface->surface(), &SurfaceInterface::unmapped,
0063             this, &LayerShellV1Window::handleUnmapped);
0064     connect(shellSurface->surface(), &SurfaceInterface::committed,
0065             this, &LayerShellV1Window::handleCommitted);
0066 
0067     connect(shellSurface, &LayerSurfaceV1Interface::desiredSizeChanged,
0068             this, &LayerShellV1Window::scheduleRearrange);
0069     connect(shellSurface, &LayerSurfaceV1Interface::layerChanged,
0070             this, &LayerShellV1Window::scheduleRearrange);
0071     connect(shellSurface, &LayerSurfaceV1Interface::marginsChanged,
0072             this, &LayerShellV1Window::scheduleRearrange);
0073     connect(shellSurface, &LayerSurfaceV1Interface::anchorChanged,
0074             this, &LayerShellV1Window::scheduleRearrange);
0075     connect(shellSurface, &LayerSurfaceV1Interface::exclusiveZoneChanged,
0076             this, &LayerShellV1Window::scheduleRearrange);
0077     connect(shellSurface, &LayerSurfaceV1Interface::acceptsFocusChanged,
0078             this, &LayerShellV1Window::handleAcceptsFocusChanged);
0079 }
0080 
0081 LayerSurfaceV1Interface *LayerShellV1Window::shellSurface() const
0082 {
0083     return m_shellSurface;
0084 }
0085 
0086 Output *LayerShellV1Window::desiredOutput() const
0087 {
0088     return m_desiredOutput;
0089 }
0090 
0091 void LayerShellV1Window::scheduleRearrange()
0092 {
0093     m_integration->scheduleRearrange();
0094 }
0095 
0096 NET::WindowType LayerShellV1Window::windowType() const
0097 {
0098     return m_windowType;
0099 }
0100 
0101 bool LayerShellV1Window::isPlaceable() const
0102 {
0103     return false;
0104 }
0105 
0106 bool LayerShellV1Window::isCloseable() const
0107 {
0108     return true;
0109 }
0110 
0111 bool LayerShellV1Window::isMovable() const
0112 {
0113     return false;
0114 }
0115 
0116 bool LayerShellV1Window::isMovableAcrossScreens() const
0117 {
0118     return false;
0119 }
0120 
0121 bool LayerShellV1Window::isResizable() const
0122 {
0123     return false;
0124 }
0125 
0126 bool LayerShellV1Window::takeFocus()
0127 {
0128     if (acceptsFocus()) {
0129         setActive(true);
0130     }
0131     return true;
0132 }
0133 
0134 bool LayerShellV1Window::wantsInput() const
0135 {
0136     return acceptsFocus() && readyForPainting();
0137 }
0138 
0139 bool LayerShellV1Window::dockWantsInput() const
0140 {
0141     return wantsInput();
0142 }
0143 
0144 StrutRect LayerShellV1Window::strutRect(StrutArea area) const
0145 {
0146     switch (area) {
0147     case StrutAreaLeft:
0148         if (m_shellSurface->exclusiveEdge() == Qt::LeftEdge) {
0149             return StrutRect(x(), y(), m_shellSurface->exclusiveZone(), height(), StrutAreaLeft);
0150         }
0151         return StrutRect();
0152     case StrutAreaRight:
0153         if (m_shellSurface->exclusiveEdge() == Qt::RightEdge) {
0154             return StrutRect(x() + width() - m_shellSurface->exclusiveZone(), y(),
0155                              m_shellSurface->exclusiveZone(), height(), StrutAreaRight);
0156         }
0157         return StrutRect();
0158     case StrutAreaTop:
0159         if (m_shellSurface->exclusiveEdge() == Qt::TopEdge) {
0160             return StrutRect(x(), y(), width(), m_shellSurface->exclusiveZone(), StrutAreaTop);
0161         }
0162         return StrutRect();
0163     case StrutAreaBottom:
0164         if (m_shellSurface->exclusiveEdge() == Qt::BottomEdge) {
0165             return StrutRect(x(), y() + height() - m_shellSurface->exclusiveZone(),
0166                              width(), m_shellSurface->exclusiveZone(), StrutAreaBottom);
0167         }
0168         return StrutRect();
0169     default:
0170         return StrutRect();
0171     }
0172 }
0173 
0174 bool LayerShellV1Window::hasStrut() const
0175 {
0176     return m_shellSurface->exclusiveZone() > 0;
0177 }
0178 
0179 void LayerShellV1Window::destroyWindow()
0180 {
0181     if (m_screenEdge) {
0182         m_screenEdge->disconnect(this);
0183     }
0184     m_shellSurface->disconnect(this);
0185     m_shellSurface->surface()->disconnect(this);
0186     m_desiredOutput->disconnect(this);
0187 
0188     markAsDeleted();
0189     cleanTabBox();
0190     Q_EMIT closed();
0191     StackingUpdatesBlocker blocker(workspace());
0192     cleanGrouping();
0193     waylandServer()->removeWindow(this);
0194     scheduleRearrange();
0195     unref();
0196 }
0197 
0198 void LayerShellV1Window::closeWindow()
0199 {
0200     m_shellSurface->sendClosed();
0201 }
0202 
0203 Layer LayerShellV1Window::belongsToLayer() const
0204 {
0205     switch (m_shellSurface->layer()) {
0206     case LayerSurfaceV1Interface::BackgroundLayer:
0207         return DesktopLayer;
0208     case LayerSurfaceV1Interface::BottomLayer:
0209         return BelowLayer;
0210     case LayerSurfaceV1Interface::TopLayer:
0211         return AboveLayer;
0212     case LayerSurfaceV1Interface::OverlayLayer:
0213         return OverlayLayer;
0214     default:
0215         Q_UNREACHABLE();
0216     }
0217 }
0218 
0219 bool LayerShellV1Window::acceptsFocus() const
0220 {
0221     return !isDeleted() && m_shellSurface->acceptsFocus();
0222 }
0223 
0224 void LayerShellV1Window::moveResizeInternal(const QRectF &rect, MoveResizeMode mode)
0225 {
0226     if (areGeometryUpdatesBlocked()) {
0227         setPendingMoveResizeMode(mode);
0228         return;
0229     }
0230 
0231     const QSizeF requestedClientSize = frameSizeToClientSize(rect.size());
0232     if (requestedClientSize != clientSize()) {
0233         m_shellSurface->sendConfigure(rect.size().toSize());
0234     } else {
0235         updateGeometry(rect);
0236         return;
0237     }
0238 
0239     // The surface position is updated synchronously.
0240     QRectF updateRect = m_frameGeometry;
0241     updateRect.moveTopLeft(rect.topLeft());
0242     updateGeometry(updateRect);
0243 }
0244 
0245 void LayerShellV1Window::handleSizeChanged()
0246 {
0247     updateGeometry(QRectF(pos(), clientSizeToFrameSize(surface()->size())));
0248     scheduleRearrange();
0249 }
0250 
0251 void LayerShellV1Window::handleUnmapped()
0252 {
0253     m_integration->recreateWindow(shellSurface());
0254 }
0255 
0256 void LayerShellV1Window::handleCommitted()
0257 {
0258     if (surface()->buffer()) {
0259         markAsMapped();
0260     }
0261 }
0262 
0263 void LayerShellV1Window::handleAcceptsFocusChanged()
0264 {
0265     switch (m_shellSurface->layer()) {
0266     case LayerSurfaceV1Interface::TopLayer:
0267     case LayerSurfaceV1Interface::OverlayLayer:
0268         if (wantsInput()) {
0269             workspace()->activateWindow(this);
0270         }
0271         break;
0272     case LayerSurfaceV1Interface::BackgroundLayer:
0273     case LayerSurfaceV1Interface::BottomLayer:
0274         break;
0275     }
0276 }
0277 
0278 void LayerShellV1Window::handleOutputEnabledChanged()
0279 {
0280     if (!m_desiredOutput->isEnabled()) {
0281         closeWindow();
0282         destroyWindow();
0283     }
0284 }
0285 
0286 void LayerShellV1Window::setVirtualKeyboardGeometry(const QRectF &geo)
0287 {
0288     if (m_virtualKeyboardGeometry == geo) {
0289         return;
0290     }
0291 
0292     m_virtualKeyboardGeometry = geo;
0293     scheduleRearrange();
0294 }
0295 
0296 void LayerShellV1Window::showOnScreenEdge()
0297 {
0298     // ShowOnScreenEdge can be called by an Edge, and setHidden could destroy the Edge
0299     // Use the singleshot to avoid use-after-free
0300     QTimer::singleShot(0, this, &LayerShellV1Window::deactivateScreenEdge);
0301 }
0302 
0303 void LayerShellV1Window::installAutoHideScreenEdgeV1(AutoHideScreenEdgeV1Interface *edge)
0304 {
0305     m_screenEdge = edge;
0306 
0307     connect(edge, &AutoHideScreenEdgeV1Interface::destroyed,
0308             this, &LayerShellV1Window::deactivateScreenEdge);
0309     connect(edge, &AutoHideScreenEdgeV1Interface::activateRequested,
0310             this, &LayerShellV1Window::activateScreenEdge);
0311     connect(edge, &AutoHideScreenEdgeV1Interface::deactivateRequested,
0312             this, &LayerShellV1Window::deactivateScreenEdge);
0313 
0314     connect(this, &LayerShellV1Window::frameGeometryChanged, edge, [this]() {
0315         if (m_screenEdgeActive) {
0316             reserveScreenEdge();
0317         }
0318     });
0319 }
0320 
0321 void LayerShellV1Window::reserveScreenEdge()
0322 {
0323     if (workspace()->screenEdges()->reserve(this, m_screenEdge->border())) {
0324         setHidden(true);
0325     } else {
0326         setHidden(false);
0327     }
0328 }
0329 
0330 void LayerShellV1Window::unreserveScreenEdge()
0331 {
0332     setHidden(false);
0333     workspace()->screenEdges()->reserve(this, ElectricNone);
0334 }
0335 
0336 void LayerShellV1Window::activateScreenEdge()
0337 {
0338     m_screenEdgeActive = true;
0339     reserveScreenEdge();
0340 }
0341 
0342 void LayerShellV1Window::deactivateScreenEdge()
0343 {
0344     m_screenEdgeActive = false;
0345     unreserveScreenEdge();
0346 }
0347 
0348 } // namespace KWin
0349 
0350 #include "moc_layershellv1window.cpp"