File indexing completed on 2024-11-10 04:57:49
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.h" 0012 #include "wayland/output.h" 0013 #include "wayland_server.h" 0014 #include "workspace.h" 0015 0016 #include <QTimer> 0017 0018 namespace KWin 0019 { 0020 0021 static const Qt::Edges AnchorHorizontal = Qt::LeftEdge | Qt::RightEdge; 0022 static const Qt::Edges AnchorVertical = Qt::TopEdge | Qt::BottomEdge; 0023 0024 LayerShellV1Integration::LayerShellV1Integration(QObject *parent) 0025 : WaylandShellIntegration(parent) 0026 { 0027 LayerShellV1Interface *shell = new LayerShellV1Interface(waylandServer()->display(), this); 0028 connect(shell, &LayerShellV1Interface::surfaceCreated, 0029 this, &LayerShellV1Integration::createWindow); 0030 0031 m_rearrangeTimer = new QTimer(this); 0032 m_rearrangeTimer->setSingleShot(true); 0033 connect(m_rearrangeTimer, &QTimer::timeout, this, &LayerShellV1Integration::rearrange); 0034 } 0035 0036 void LayerShellV1Integration::createWindow(LayerSurfaceV1Interface *shellSurface) 0037 { 0038 Output *output = shellSurface->output() ? shellSurface->output()->handle() : workspace()->activeOutput(); 0039 if (!output) { 0040 qCWarning(KWIN_CORE) << "Could not find any suitable output for a layer surface"; 0041 shellSurface->sendClosed(); 0042 return; 0043 } 0044 0045 Q_EMIT windowCreated(new LayerShellV1Window(shellSurface, output, this)); 0046 } 0047 0048 void LayerShellV1Integration::recreateWindow(LayerSurfaceV1Interface *shellSurface) 0049 { 0050 destroyWindow(shellSurface); 0051 createWindow(shellSurface); 0052 } 0053 0054 void LayerShellV1Integration::destroyWindow(LayerSurfaceV1Interface *shellSurface) 0055 { 0056 const QList<Window *> windows = waylandServer()->windows(); 0057 for (Window *window : windows) { 0058 LayerShellV1Window *layerShellWindow = qobject_cast<LayerShellV1Window *>(window); 0059 if (layerShellWindow && layerShellWindow->shellSurface() == shellSurface) { 0060 layerShellWindow->destroyWindow(); 0061 break; 0062 } 0063 } 0064 } 0065 0066 static void adjustWorkArea(const LayerSurfaceV1Interface *shellSurface, QRect *workArea) 0067 { 0068 switch (shellSurface->exclusiveEdge()) { 0069 case Qt::LeftEdge: 0070 workArea->adjust(shellSurface->leftMargin() + shellSurface->exclusiveZone(), 0, 0, 0); 0071 break; 0072 case Qt::RightEdge: 0073 workArea->adjust(0, 0, -shellSurface->rightMargin() - shellSurface->exclusiveZone(), 0); 0074 break; 0075 case Qt::TopEdge: 0076 workArea->adjust(0, shellSurface->topMargin() + shellSurface->exclusiveZone(), 0, 0); 0077 break; 0078 case Qt::BottomEdge: 0079 workArea->adjust(0, 0, 0, -shellSurface->bottomMargin() - shellSurface->exclusiveZone()); 0080 break; 0081 } 0082 } 0083 0084 static void rearrangeLayer(const QList<LayerShellV1Window *> &windows, QRect *workArea, 0085 LayerSurfaceV1Interface::Layer layer, bool exclusive) 0086 { 0087 for (LayerShellV1Window *window : windows) { 0088 LayerSurfaceV1Interface *shellSurface = window->shellSurface(); 0089 0090 if (shellSurface->layer() != layer) { 0091 continue; 0092 } 0093 if (exclusive != (shellSurface->exclusiveZone() > 0)) { 0094 continue; 0095 } 0096 0097 QRect bounds; 0098 if (shellSurface->exclusiveZone() == -1) { 0099 bounds = window->desiredOutput()->geometry(); 0100 } else { 0101 bounds = *workArea; 0102 } 0103 0104 QRect geometry(QPoint(0, 0), shellSurface->desiredSize()); 0105 0106 if ((shellSurface->anchor() & AnchorHorizontal) && geometry.width() == 0) { 0107 geometry.setLeft(bounds.left()); 0108 geometry.setWidth(bounds.width()); 0109 } else if (shellSurface->anchor() & Qt::LeftEdge) { 0110 geometry.moveLeft(bounds.left()); 0111 } else if (shellSurface->anchor() & Qt::RightEdge) { 0112 geometry.moveRight(bounds.right()); 0113 } else { 0114 geometry.moveLeft(bounds.left() + (bounds.width() - geometry.width()) / 2); 0115 } 0116 0117 if ((shellSurface->anchor() & AnchorVertical) && geometry.height() == 0) { 0118 geometry.setTop(bounds.top()); 0119 geometry.setHeight(bounds.height()); 0120 } else if (shellSurface->anchor() & Qt::TopEdge) { 0121 geometry.moveTop(bounds.top()); 0122 } else if (shellSurface->anchor() & Qt::BottomEdge) { 0123 geometry.moveBottom(bounds.bottom()); 0124 } else { 0125 geometry.moveTop(bounds.top() + (bounds.height() - geometry.height()) / 2); 0126 } 0127 0128 if ((shellSurface->anchor() & AnchorHorizontal) == AnchorHorizontal) { 0129 geometry.adjust(shellSurface->leftMargin(), 0, -shellSurface->rightMargin(), 0); 0130 } else if (shellSurface->anchor() & Qt::LeftEdge) { 0131 geometry.translate(shellSurface->leftMargin(), 0); 0132 } else if (shellSurface->anchor() & Qt::RightEdge) { 0133 geometry.translate(-shellSurface->rightMargin(), 0); 0134 } 0135 0136 if ((shellSurface->anchor() & AnchorVertical) == AnchorVertical) { 0137 geometry.adjust(0, shellSurface->topMargin(), 0, -shellSurface->bottomMargin()); 0138 } else if (shellSurface->anchor() & Qt::TopEdge) { 0139 geometry.translate(0, shellSurface->topMargin()); 0140 } else if (shellSurface->anchor() & Qt::BottomEdge) { 0141 geometry.translate(0, -shellSurface->bottomMargin()); 0142 } 0143 0144 // Move the window's bottom if its virtual keyboard is overlapping it 0145 if (shellSurface->exclusiveZone() >= 0 && !window->virtualKeyboardGeometry().isEmpty() && geometry.bottom() > window->virtualKeyboardGeometry().top()) { 0146 geometry.setBottom(window->virtualKeyboardGeometry().top()); 0147 } 0148 0149 window->updateLayer(); 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 0219 0220 #include "moc_layershellv1integration.cpp"