File indexing completed on 2024-12-08 13:21:55
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 "layershellv1integration.h" 0008 #include "core/output.h" 0009 #include "layershellv1window.h" 0010 #include "wayland/display.h" 0011 #include "wayland/layershell_v1_interface.h" 0012 #include "wayland/output_interface.h" 0013 #include "wayland_server.h" 0014 #include "workspace.h" 0015 0016 #include <QTimer> 0017 0018 using namespace KWaylandServer; 0019 0020 namespace KWin 0021 { 0022 0023 static const Qt::Edges AnchorHorizontal = Qt::LeftEdge | Qt::RightEdge; 0024 static const Qt::Edges AnchorVertical = Qt::TopEdge | Qt::BottomEdge; 0025 0026 LayerShellV1Integration::LayerShellV1Integration(QObject *parent) 0027 : WaylandShellIntegration(parent) 0028 { 0029 LayerShellV1Interface *shell = new LayerShellV1Interface(waylandServer()->display(), this); 0030 connect(shell, &KWaylandServer::LayerShellV1Interface::surfaceCreated, 0031 this, &LayerShellV1Integration::createWindow); 0032 0033 m_rearrangeTimer = new QTimer(this); 0034 m_rearrangeTimer->setSingleShot(true); 0035 connect(m_rearrangeTimer, &QTimer::timeout, this, &LayerShellV1Integration::rearrange); 0036 } 0037 0038 void LayerShellV1Integration::createWindow(LayerSurfaceV1Interface *shellSurface) 0039 { 0040 Output *output = shellSurface->output() ? shellSurface->output()->handle() : workspace()->activeOutput(); 0041 if (!output) { 0042 qCWarning(KWIN_CORE) << "Could not find any suitable output for a layer surface"; 0043 shellSurface->sendClosed(); 0044 return; 0045 } 0046 0047 Q_EMIT windowCreated(new LayerShellV1Window(shellSurface, output, this)); 0048 } 0049 0050 void LayerShellV1Integration::recreateWindow(LayerSurfaceV1Interface *shellSurface) 0051 { 0052 destroyWindow(shellSurface); 0053 createWindow(shellSurface); 0054 } 0055 0056 void LayerShellV1Integration::destroyWindow(LayerSurfaceV1Interface *shellSurface) 0057 { 0058 const QList<Window *> windows = waylandServer()->windows(); 0059 for (Window *window : windows) { 0060 LayerShellV1Window *layerShellWindow = qobject_cast<LayerShellV1Window *>(window); 0061 if (layerShellWindow && layerShellWindow->shellSurface() == shellSurface) { 0062 layerShellWindow->destroyWindow(); 0063 break; 0064 } 0065 } 0066 } 0067 0068 static void adjustWorkArea(const LayerSurfaceV1Interface *shellSurface, QRect *workArea) 0069 { 0070 switch (shellSurface->exclusiveEdge()) { 0071 case Qt::LeftEdge: 0072 workArea->adjust(shellSurface->leftMargin() + shellSurface->exclusiveZone(), 0, 0, 0); 0073 break; 0074 case Qt::RightEdge: 0075 workArea->adjust(0, 0, -shellSurface->rightMargin() - shellSurface->exclusiveZone(), 0); 0076 break; 0077 case Qt::TopEdge: 0078 workArea->adjust(0, shellSurface->topMargin() + shellSurface->exclusiveZone(), 0, 0); 0079 break; 0080 case Qt::BottomEdge: 0081 workArea->adjust(0, 0, 0, -shellSurface->bottomMargin() - shellSurface->exclusiveZone()); 0082 break; 0083 } 0084 } 0085 0086 static void rearrangeLayer(const QList<LayerShellV1Window *> &windows, QRect *workArea, 0087 LayerSurfaceV1Interface::Layer layer, bool exclusive) 0088 { 0089 for (LayerShellV1Window *window : windows) { 0090 LayerSurfaceV1Interface *shellSurface = window->shellSurface(); 0091 0092 if (shellSurface->layer() != layer) { 0093 continue; 0094 } 0095 if (exclusive != (shellSurface->exclusiveZone() > 0)) { 0096 continue; 0097 } 0098 0099 QRect bounds; 0100 if (shellSurface->exclusiveZone() == -1) { 0101 bounds = window->desiredOutput()->geometry(); 0102 } else { 0103 bounds = *workArea; 0104 } 0105 0106 QRect geometry(QPoint(0, 0), shellSurface->desiredSize()); 0107 0108 if ((shellSurface->anchor() & AnchorHorizontal) && geometry.width() == 0) { 0109 geometry.setLeft(bounds.left()); 0110 geometry.setWidth(bounds.width()); 0111 } else if (shellSurface->anchor() & Qt::LeftEdge) { 0112 geometry.moveLeft(bounds.left()); 0113 } else if (shellSurface->anchor() & Qt::RightEdge) { 0114 geometry.moveRight(bounds.right()); 0115 } else { 0116 geometry.moveLeft(bounds.left() + (bounds.width() - geometry.width()) / 2); 0117 } 0118 0119 if ((shellSurface->anchor() & AnchorVertical) && geometry.height() == 0) { 0120 geometry.setTop(bounds.top()); 0121 geometry.setHeight(bounds.height()); 0122 } else if (shellSurface->anchor() & Qt::TopEdge) { 0123 geometry.moveTop(bounds.top()); 0124 } else if (shellSurface->anchor() & Qt::BottomEdge) { 0125 geometry.moveBottom(bounds.bottom()); 0126 } else { 0127 geometry.moveTop(bounds.top() + (bounds.height() - geometry.height()) / 2); 0128 } 0129 0130 if ((shellSurface->anchor() & AnchorHorizontal) == AnchorHorizontal) { 0131 geometry.adjust(shellSurface->leftMargin(), 0, -shellSurface->rightMargin(), 0); 0132 } else if (shellSurface->anchor() & Qt::LeftEdge) { 0133 geometry.translate(shellSurface->leftMargin(), 0); 0134 } else if (shellSurface->anchor() & Qt::RightEdge) { 0135 geometry.translate(-shellSurface->rightMargin(), 0); 0136 } 0137 0138 if ((shellSurface->anchor() & AnchorVertical) == AnchorVertical) { 0139 geometry.adjust(0, shellSurface->topMargin(), 0, -shellSurface->bottomMargin()); 0140 } else if (shellSurface->anchor() & Qt::TopEdge) { 0141 geometry.translate(0, shellSurface->topMargin()); 0142 } else if (shellSurface->anchor() & Qt::BottomEdge) { 0143 geometry.translate(0, -shellSurface->bottomMargin()); 0144 } 0145 0146 // Move the window's bottom if its virtual keyboard is overlapping it 0147 if (shellSurface->exclusiveZone() >= 0 && !window->virtualKeyboardGeometry().isEmpty() && geometry.bottom() > window->virtualKeyboardGeometry().top()) { 0148 geometry.setBottom(window->virtualKeyboardGeometry().top()); 0149 } 0150 0151 if (geometry.isValid()) { 0152 window->moveResize(geometry); 0153 } else { 0154 qCWarning(KWIN_CORE) << "Closing a layer shell window due to invalid geometry"; 0155 window->closeWindow(); 0156 continue; 0157 } 0158 0159 if (exclusive && shellSurface->exclusiveZone() > 0) { 0160 adjustWorkArea(shellSurface, workArea); 0161 } 0162 } 0163 } 0164 0165 static QList<LayerShellV1Window *> windowsForOutput(Output *output) 0166 { 0167 QList<LayerShellV1Window *> result; 0168 const QList<Window *> windows = waylandServer()->windows(); 0169 for (Window *window : windows) { 0170 LayerShellV1Window *layerShellWindow = qobject_cast<LayerShellV1Window *>(window); 0171 if (!layerShellWindow || layerShellWindow->desiredOutput() != output) { 0172 continue; 0173 } 0174 if (layerShellWindow->shellSurface()->isCommitted()) { 0175 result.append(layerShellWindow); 0176 } 0177 } 0178 return result; 0179 } 0180 0181 static void rearrangeOutput(Output *output) 0182 { 0183 const QList<LayerShellV1Window *> windows = windowsForOutput(output); 0184 if (!windows.isEmpty()) { 0185 QRect workArea = output->geometry(); 0186 0187 rearrangeLayer(windows, &workArea, LayerSurfaceV1Interface::OverlayLayer, true); 0188 rearrangeLayer(windows, &workArea, LayerSurfaceV1Interface::TopLayer, true); 0189 rearrangeLayer(windows, &workArea, LayerSurfaceV1Interface::BottomLayer, true); 0190 rearrangeLayer(windows, &workArea, LayerSurfaceV1Interface::BackgroundLayer, true); 0191 0192 rearrangeLayer(windows, &workArea, LayerSurfaceV1Interface::OverlayLayer, false); 0193 rearrangeLayer(windows, &workArea, LayerSurfaceV1Interface::TopLayer, false); 0194 rearrangeLayer(windows, &workArea, LayerSurfaceV1Interface::BottomLayer, false); 0195 rearrangeLayer(windows, &workArea, LayerSurfaceV1Interface::BackgroundLayer, false); 0196 } 0197 } 0198 0199 void LayerShellV1Integration::rearrange() 0200 { 0201 m_rearrangeTimer->stop(); 0202 0203 const QList<Output *> outputs = workspace()->outputs(); 0204 for (Output *output : outputs) { 0205 rearrangeOutput(output); 0206 } 0207 0208 if (workspace()) { 0209 workspace()->updateClientArea(); 0210 } 0211 } 0212 0213 void LayerShellV1Integration::scheduleRearrange() 0214 { 0215 m_rearrangeTimer->start(); 0216 } 0217 0218 } // namespace KWin