File indexing completed on 2024-04-28 16:48:53

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 "deleted.h"
0010 #include "layershellv1integration.h"
0011 #include "wayland/layershell_v1_interface.h"
0012 #include "wayland/output_interface.h"
0013 #include "wayland/surface_interface.h"
0014 #include "wayland_server.h"
0015 #include "workspace.h"
0016 
0017 using namespace KWaylandServer;
0018 
0019 namespace KWin
0020 {
0021 
0022 static NET::WindowType scopeToType(const QString &scope)
0023 {
0024     static const QHash<QString, NET::WindowType> scopeToType{
0025         {QStringLiteral("desktop"), NET::Desktop},
0026         {QStringLiteral("dock"), NET::Dock},
0027         {QStringLiteral("crititical-notification"), NET::CriticalNotification},
0028         {QStringLiteral("notification"), NET::Notification},
0029         {QStringLiteral("tooltip"), NET::Tooltip},
0030         {QStringLiteral("on-screen-display"), NET::OnScreenDisplay},
0031         {QStringLiteral("dialog"), NET::Dialog},
0032         {QStringLiteral("splash"), NET::Splash},
0033         {QStringLiteral("utility"), NET::Utility},
0034     };
0035     return scopeToType.value(scope.toLower(), NET::Normal);
0036 }
0037 
0038 LayerShellV1Window::LayerShellV1Window(LayerSurfaceV1Interface *shellSurface,
0039                                        Output *output,
0040                                        LayerShellV1Integration *integration)
0041     : WaylandWindow(shellSurface->surface())
0042     , m_desiredOutput(output)
0043     , m_integration(integration)
0044     , m_shellSurface(shellSurface)
0045     , m_windowType(scopeToType(shellSurface->scope()))
0046 {
0047     setSkipSwitcher(!isDesktop());
0048     setSkipPager(true);
0049     setSkipTaskbar(true);
0050 
0051     connect(shellSurface, &LayerSurfaceV1Interface::aboutToBeDestroyed,
0052             this, &LayerShellV1Window::destroyWindow);
0053     connect(shellSurface->surface(), &SurfaceInterface::aboutToBeDestroyed,
0054             this, &LayerShellV1Window::destroyWindow);
0055 
0056     connect(output, &Output::geometryChanged,
0057             this, &LayerShellV1Window::scheduleRearrange);
0058     connect(output, &Output::enabledChanged,
0059             this, &LayerShellV1Window::handleOutputEnabledChanged);
0060     connect(output, &Output::destroyed,
0061             this, &LayerShellV1Window::handleOutputDestroyed);
0062 
0063     connect(shellSurface->surface(), &SurfaceInterface::sizeChanged,
0064             this, &LayerShellV1Window::handleSizeChanged);
0065     connect(shellSurface->surface(), &SurfaceInterface::unmapped,
0066             this, &LayerShellV1Window::handleUnmapped);
0067     connect(shellSurface->surface(), &SurfaceInterface::committed,
0068             this, &LayerShellV1Window::handleCommitted);
0069 
0070     connect(shellSurface, &LayerSurfaceV1Interface::desiredSizeChanged,
0071             this, &LayerShellV1Window::scheduleRearrange);
0072     connect(shellSurface, &LayerSurfaceV1Interface::layerChanged,
0073             this, &LayerShellV1Window::scheduleRearrange);
0074     connect(shellSurface, &LayerSurfaceV1Interface::marginsChanged,
0075             this, &LayerShellV1Window::scheduleRearrange);
0076     connect(shellSurface, &LayerSurfaceV1Interface::anchorChanged,
0077             this, &LayerShellV1Window::scheduleRearrange);
0078     connect(shellSurface, &LayerSurfaceV1Interface::exclusiveZoneChanged,
0079             this, &LayerShellV1Window::scheduleRearrange);
0080     connect(shellSurface, &LayerSurfaceV1Interface::acceptsFocusChanged,
0081             this, &LayerShellV1Window::handleAcceptsFocusChanged);
0082 }
0083 
0084 LayerSurfaceV1Interface *LayerShellV1Window::shellSurface() const
0085 {
0086     return m_shellSurface;
0087 }
0088 
0089 Output *LayerShellV1Window::desiredOutput() const
0090 {
0091     return m_desiredOutput;
0092 }
0093 
0094 void LayerShellV1Window::scheduleRearrange()
0095 {
0096     m_integration->scheduleRearrange();
0097 }
0098 
0099 NET::WindowType LayerShellV1Window::windowType(bool, int) const
0100 {
0101     return m_windowType;
0102 }
0103 
0104 bool LayerShellV1Window::isPlaceable() const
0105 {
0106     return false;
0107 }
0108 
0109 bool LayerShellV1Window::isCloseable() const
0110 {
0111     return true;
0112 }
0113 
0114 bool LayerShellV1Window::isMovable() const
0115 {
0116     return false;
0117 }
0118 
0119 bool LayerShellV1Window::isMovableAcrossScreens() const
0120 {
0121     return false;
0122 }
0123 
0124 bool LayerShellV1Window::isResizable() const
0125 {
0126     return false;
0127 }
0128 
0129 bool LayerShellV1Window::takeFocus()
0130 {
0131     setActive(true);
0132     return true;
0133 }
0134 
0135 bool LayerShellV1Window::wantsInput() const
0136 {
0137     return acceptsFocus() && readyForPainting();
0138 }
0139 
0140 StrutRect LayerShellV1Window::strutRect(StrutArea area) const
0141 {
0142     switch (area) {
0143     case StrutAreaLeft:
0144         if (m_shellSurface->exclusiveEdge() == Qt::LeftEdge) {
0145             return StrutRect(x(), y(), m_shellSurface->exclusiveZone(), height(), StrutAreaLeft);
0146         }
0147         return StrutRect();
0148     case StrutAreaRight:
0149         if (m_shellSurface->exclusiveEdge() == Qt::RightEdge) {
0150             return StrutRect(x() + width() - m_shellSurface->exclusiveZone(), y(),
0151                              m_shellSurface->exclusiveZone(), height(), StrutAreaRight);
0152         }
0153         return StrutRect();
0154     case StrutAreaTop:
0155         if (m_shellSurface->exclusiveEdge() == Qt::TopEdge) {
0156             return StrutRect(x(), y(), width(), m_shellSurface->exclusiveZone(), StrutAreaTop);
0157         }
0158         return StrutRect();
0159     case StrutAreaBottom:
0160         if (m_shellSurface->exclusiveEdge() == Qt::BottomEdge) {
0161             return StrutRect(x(), y() + height() - m_shellSurface->exclusiveZone(),
0162                              width(), m_shellSurface->exclusiveZone(), StrutAreaBottom);
0163         }
0164         return StrutRect();
0165     default:
0166         return StrutRect();
0167     }
0168 }
0169 
0170 bool LayerShellV1Window::hasStrut() const
0171 {
0172     return m_shellSurface->exclusiveZone() > 0;
0173 }
0174 
0175 void LayerShellV1Window::destroyWindow()
0176 {
0177     markAsZombie();
0178     cleanTabBox();
0179     Deleted *deleted = Deleted::create(this);
0180     Q_EMIT windowClosed(this, deleted);
0181     StackingUpdatesBlocker blocker(workspace());
0182     cleanGrouping();
0183     waylandServer()->removeWindow(this);
0184     deleted->unrefWindow();
0185     scheduleRearrange();
0186     delete this;
0187 }
0188 
0189 void LayerShellV1Window::closeWindow()
0190 {
0191     m_shellSurface->sendClosed();
0192 }
0193 
0194 Layer LayerShellV1Window::belongsToLayer() const
0195 {
0196     if (!isNormalWindow()) {
0197         return WaylandWindow::belongsToLayer();
0198     }
0199     switch (m_shellSurface->layer()) {
0200     case LayerSurfaceV1Interface::BackgroundLayer:
0201         return DesktopLayer;
0202     case LayerSurfaceV1Interface::BottomLayer:
0203         return BelowLayer;
0204     case LayerSurfaceV1Interface::TopLayer:
0205         return AboveLayer;
0206     case LayerSurfaceV1Interface::OverlayLayer:
0207         return UnmanagedLayer;
0208     default:
0209         Q_UNREACHABLE();
0210     }
0211 }
0212 
0213 bool LayerShellV1Window::acceptsFocus() const
0214 {
0215     return !isDeleted() && m_shellSurface->acceptsFocus();
0216 }
0217 
0218 void LayerShellV1Window::moveResizeInternal(const QRectF &rect, MoveResizeMode mode)
0219 {
0220     if (areGeometryUpdatesBlocked()) {
0221         setPendingMoveResizeMode(mode);
0222         return;
0223     }
0224 
0225     const QSizeF requestedClientSize = frameSizeToClientSize(rect.size());
0226     if (requestedClientSize != clientSize()) {
0227         m_shellSurface->sendConfigure(rect.size().toSize());
0228     } else {
0229         updateGeometry(rect);
0230         return;
0231     }
0232 
0233     // The surface position is updated synchronously.
0234     QRectF updateRect = m_frameGeometry;
0235     updateRect.moveTopLeft(rect.topLeft());
0236     updateGeometry(updateRect);
0237 }
0238 
0239 void LayerShellV1Window::handleSizeChanged()
0240 {
0241     updateGeometry(QRectF(pos(), clientSizeToFrameSize(surface()->size())));
0242     scheduleRearrange();
0243 }
0244 
0245 void LayerShellV1Window::handleUnmapped()
0246 {
0247     m_integration->recreateWindow(shellSurface());
0248 }
0249 
0250 void LayerShellV1Window::handleCommitted()
0251 {
0252     if (surface()->buffer()) {
0253         updateDepth();
0254         setReadyForPainting();
0255     }
0256 }
0257 
0258 void LayerShellV1Window::handleAcceptsFocusChanged()
0259 {
0260     switch (m_shellSurface->layer()) {
0261     case LayerSurfaceV1Interface::TopLayer:
0262     case LayerSurfaceV1Interface::OverlayLayer:
0263         if (wantsInput()) {
0264             workspace()->activateWindow(this);
0265         }
0266         break;
0267     case LayerSurfaceV1Interface::BackgroundLayer:
0268     case LayerSurfaceV1Interface::BottomLayer:
0269         break;
0270     }
0271 }
0272 
0273 void LayerShellV1Window::handleOutputEnabledChanged()
0274 {
0275     if (!m_desiredOutput->isEnabled()) {
0276         closeWindow();
0277         destroyWindow();
0278     }
0279 }
0280 
0281 void LayerShellV1Window::handleOutputDestroyed()
0282 {
0283     closeWindow();
0284     destroyWindow();
0285 }
0286 
0287 void LayerShellV1Window::setVirtualKeyboardGeometry(const QRectF &geo)
0288 {
0289     if (m_virtualKeyboardGeometry == geo) {
0290         return;
0291     }
0292 
0293     m_virtualKeyboardGeometry = geo;
0294     scheduleRearrange();
0295 }
0296 
0297 } // namespace KWin