2020-08-07 19:01:42 +00:00
|
|
|
/*
|
|
|
|
SPDX-FileCopyrightText: 2020 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
|
|
|
|
|
|
|
|
SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "layershellv1integration.h"
|
2022-04-22 17:46:41 +00:00
|
|
|
#include "layershellv1window.h"
|
2022-04-14 12:33:28 +00:00
|
|
|
#include "output.h"
|
2020-08-07 19:01:42 +00:00
|
|
|
#include "platform.h"
|
|
|
|
#include "screens.h"
|
2022-04-22 09:27:33 +00:00
|
|
|
#include "wayland/display.h"
|
|
|
|
#include "wayland/layershell_v1_interface.h"
|
2020-08-07 19:01:42 +00:00
|
|
|
#include "wayland_server.h"
|
|
|
|
#include "workspace.h"
|
|
|
|
|
|
|
|
#include <QTimer>
|
|
|
|
|
|
|
|
using namespace KWaylandServer;
|
|
|
|
|
|
|
|
namespace KWin
|
|
|
|
{
|
|
|
|
|
|
|
|
static const Qt::Edges AnchorHorizontal = Qt::LeftEdge | Qt::RightEdge;
|
|
|
|
static const Qt::Edges AnchorVertical = Qt::TopEdge | Qt::BottomEdge;
|
|
|
|
|
|
|
|
LayerShellV1Integration::LayerShellV1Integration(QObject *parent)
|
|
|
|
: WaylandShellIntegration(parent)
|
|
|
|
{
|
2020-12-09 21:24:41 +00:00
|
|
|
LayerShellV1Interface *shell = new LayerShellV1Interface(waylandServer()->display(), this);
|
2020-08-07 19:01:42 +00:00
|
|
|
connect(shell, &KWaylandServer::LayerShellV1Interface::surfaceCreated,
|
2022-04-23 08:33:23 +00:00
|
|
|
this, &LayerShellV1Integration::createWindow);
|
2020-08-07 19:01:42 +00:00
|
|
|
|
|
|
|
m_rearrangeTimer = new QTimer(this);
|
|
|
|
m_rearrangeTimer->setSingleShot(true);
|
|
|
|
connect(m_rearrangeTimer, &QTimer::timeout, this, &LayerShellV1Integration::rearrange);
|
|
|
|
}
|
|
|
|
|
2022-04-23 08:33:23 +00:00
|
|
|
void LayerShellV1Integration::createWindow(LayerSurfaceV1Interface *shellSurface)
|
2020-08-07 19:01:42 +00:00
|
|
|
{
|
2022-04-14 12:33:28 +00:00
|
|
|
Output *output = waylandServer()->findOutput(shellSurface->output());
|
2020-08-07 19:01:42 +00:00
|
|
|
if (!output) {
|
2021-08-28 18:58:29 +00:00
|
|
|
output = workspace()->activeOutput();
|
2020-08-07 19:01:42 +00:00
|
|
|
}
|
|
|
|
if (!output) {
|
|
|
|
qCWarning(KWIN_CORE) << "Could not find any suitable output for a layer surface";
|
|
|
|
shellSurface->sendClosed();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-04-23 08:33:23 +00:00
|
|
|
Q_EMIT windowCreated(new LayerShellV1Window(shellSurface, output, this));
|
2020-08-07 19:01:42 +00:00
|
|
|
}
|
|
|
|
|
2022-04-23 08:33:23 +00:00
|
|
|
void LayerShellV1Integration::recreateWindow(LayerSurfaceV1Interface *shellSurface)
|
2020-08-07 19:01:42 +00:00
|
|
|
{
|
2022-04-23 08:33:23 +00:00
|
|
|
destroyWindow(shellSurface);
|
|
|
|
createWindow(shellSurface);
|
2020-08-07 19:01:42 +00:00
|
|
|
}
|
|
|
|
|
2022-04-23 08:33:23 +00:00
|
|
|
void LayerShellV1Integration::destroyWindow(LayerSurfaceV1Interface *shellSurface)
|
2020-08-07 19:01:42 +00:00
|
|
|
{
|
2022-04-23 08:33:23 +00:00
|
|
|
const QList<Window *> windows = waylandServer()->windows();
|
|
|
|
for (Window *window : windows) {
|
|
|
|
LayerShellV1Window *layerShellWindow = qobject_cast<LayerShellV1Window *>(window);
|
|
|
|
if (layerShellWindow && layerShellWindow->shellSurface() == shellSurface) {
|
|
|
|
layerShellWindow->destroyWindow();
|
2020-08-07 19:01:42 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void adjustWorkArea(const LayerSurfaceV1Interface *shellSurface, QRect *workArea)
|
|
|
|
{
|
2020-09-09 08:19:59 +00:00
|
|
|
switch (shellSurface->exclusiveEdge()) {
|
|
|
|
case Qt::LeftEdge:
|
2020-08-07 19:01:42 +00:00
|
|
|
workArea->adjust(shellSurface->leftMargin() + shellSurface->exclusiveZone(), 0, 0, 0);
|
2020-09-09 08:19:59 +00:00
|
|
|
break;
|
|
|
|
case Qt::RightEdge:
|
2020-08-07 19:01:42 +00:00
|
|
|
workArea->adjust(0, 0, -shellSurface->rightMargin() - shellSurface->exclusiveZone(), 0);
|
2020-09-09 08:19:59 +00:00
|
|
|
break;
|
|
|
|
case Qt::TopEdge:
|
2020-08-07 19:01:42 +00:00
|
|
|
workArea->adjust(0, shellSurface->topMargin() + shellSurface->exclusiveZone(), 0, 0);
|
2020-09-09 08:19:59 +00:00
|
|
|
break;
|
|
|
|
case Qt::BottomEdge:
|
2020-08-07 19:01:42 +00:00
|
|
|
workArea->adjust(0, 0, 0, -shellSurface->bottomMargin() - shellSurface->exclusiveZone());
|
2020-09-09 08:19:59 +00:00
|
|
|
break;
|
2020-08-07 19:01:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-23 08:33:23 +00:00
|
|
|
static void rearrangeLayer(const QList<LayerShellV1Window *> &windows, QRect *workArea,
|
2020-08-07 19:01:42 +00:00
|
|
|
LayerSurfaceV1Interface::Layer layer, bool exclusive)
|
|
|
|
{
|
2022-04-23 08:33:23 +00:00
|
|
|
for (LayerShellV1Window *window : windows) {
|
|
|
|
LayerSurfaceV1Interface *shellSurface = window->shellSurface();
|
2020-08-07 19:01:42 +00:00
|
|
|
|
|
|
|
if (shellSurface->layer() != layer) {
|
|
|
|
continue;
|
|
|
|
}
|
2020-09-09 14:05:34 +00:00
|
|
|
if (exclusive != (shellSurface->exclusiveZone() > 0)) {
|
2020-08-07 19:01:42 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
QRect bounds;
|
|
|
|
if (shellSurface->exclusiveZone() == -1) {
|
2022-04-23 08:33:23 +00:00
|
|
|
bounds = window->desiredOutput()->geometry();
|
2020-08-07 19:01:42 +00:00
|
|
|
} else {
|
|
|
|
bounds = *workArea;
|
|
|
|
}
|
|
|
|
|
|
|
|
QRect geometry(QPoint(0, 0), shellSurface->desiredSize());
|
|
|
|
|
|
|
|
if ((shellSurface->anchor() & AnchorHorizontal) && geometry.width() == 0) {
|
|
|
|
geometry.setLeft(bounds.left());
|
|
|
|
geometry.setWidth(bounds.width());
|
|
|
|
} else if (shellSurface->anchor() & Qt::LeftEdge) {
|
|
|
|
geometry.moveLeft(bounds.left());
|
|
|
|
} else if (shellSurface->anchor() & Qt::RightEdge) {
|
|
|
|
geometry.moveRight(bounds.right());
|
|
|
|
} else {
|
|
|
|
geometry.moveLeft(bounds.left() + (bounds.width() - geometry.width()) / 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((shellSurface->anchor() & AnchorVertical) && geometry.height() == 0) {
|
|
|
|
geometry.setTop(bounds.top());
|
|
|
|
geometry.setHeight(bounds.height());
|
|
|
|
} else if (shellSurface->anchor() & Qt::TopEdge) {
|
|
|
|
geometry.moveTop(bounds.top());
|
|
|
|
} else if (shellSurface->anchor() & Qt::BottomEdge) {
|
|
|
|
geometry.moveBottom(bounds.bottom());
|
|
|
|
} else {
|
|
|
|
geometry.moveTop(bounds.top() + (bounds.height() - geometry.height()) / 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((shellSurface->anchor() & AnchorHorizontal) == AnchorHorizontal) {
|
|
|
|
geometry.adjust(shellSurface->leftMargin(), 0, -shellSurface->rightMargin(), 0);
|
|
|
|
} else if (shellSurface->anchor() & Qt::LeftEdge) {
|
|
|
|
geometry.translate(shellSurface->leftMargin(), 0);
|
|
|
|
} else if (shellSurface->anchor() & Qt::RightEdge) {
|
|
|
|
geometry.translate(-shellSurface->rightMargin(), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((shellSurface->anchor() & AnchorVertical) == AnchorVertical) {
|
|
|
|
geometry.adjust(0, shellSurface->topMargin(), 0, -shellSurface->bottomMargin());
|
|
|
|
} else if (shellSurface->anchor() & Qt::TopEdge) {
|
|
|
|
geometry.translate(0, shellSurface->topMargin());
|
|
|
|
} else if (shellSurface->anchor() & Qt::BottomEdge) {
|
|
|
|
geometry.translate(0, -shellSurface->bottomMargin());
|
|
|
|
}
|
|
|
|
|
2021-04-09 14:44:44 +00:00
|
|
|
// Move the window's bottom if its virtual keyboard is overlapping it
|
2022-04-23 08:33:23 +00:00
|
|
|
if (shellSurface->exclusiveZone() >= 0 && !window->virtualKeyboardGeometry().isEmpty() && geometry.bottom() > window->virtualKeyboardGeometry().top()) {
|
|
|
|
geometry.setBottom(window->virtualKeyboardGeometry().top());
|
2021-04-09 14:44:44 +00:00
|
|
|
}
|
|
|
|
|
2020-08-07 19:01:42 +00:00
|
|
|
if (geometry.isValid()) {
|
2022-04-23 08:33:23 +00:00
|
|
|
window->moveResize(geometry);
|
2020-08-07 19:01:42 +00:00
|
|
|
} else {
|
2022-04-23 08:33:23 +00:00
|
|
|
qCWarning(KWIN_CORE) << "Closing a layer shell window due to invalid geometry";
|
|
|
|
window->closeWindow();
|
2020-08-07 19:01:42 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (exclusive && shellSurface->exclusiveZone() > 0) {
|
|
|
|
adjustWorkArea(shellSurface, workArea);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-23 08:33:23 +00:00
|
|
|
static QList<LayerShellV1Window *> windowsForOutput(Output *output)
|
2020-08-07 19:01:42 +00:00
|
|
|
{
|
2022-04-22 17:46:41 +00:00
|
|
|
QList<LayerShellV1Window *> result;
|
2022-04-23 08:33:23 +00:00
|
|
|
const QList<Window *> windows = waylandServer()->windows();
|
|
|
|
for (Window *window : windows) {
|
|
|
|
LayerShellV1Window *layerShellWindow = qobject_cast<LayerShellV1Window *>(window);
|
|
|
|
if (!layerShellWindow || layerShellWindow->desiredOutput() != output) {
|
2020-08-07 19:01:42 +00:00
|
|
|
continue;
|
|
|
|
}
|
2022-04-23 08:33:23 +00:00
|
|
|
if (layerShellWindow->shellSurface()->isCommitted()) {
|
|
|
|
result.append(layerShellWindow);
|
2020-08-07 19:01:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-04-14 12:33:28 +00:00
|
|
|
static void rearrangeOutput(Output *output)
|
2020-08-07 19:01:42 +00:00
|
|
|
{
|
2022-04-23 08:33:23 +00:00
|
|
|
const QList<LayerShellV1Window *> windows = windowsForOutput(output);
|
|
|
|
if (!windows.isEmpty()) {
|
2020-08-07 19:01:42 +00:00
|
|
|
QRect workArea = output->geometry();
|
|
|
|
|
2022-04-23 08:33:23 +00:00
|
|
|
rearrangeLayer(windows, &workArea, LayerSurfaceV1Interface::OverlayLayer, true);
|
|
|
|
rearrangeLayer(windows, &workArea, LayerSurfaceV1Interface::TopLayer, true);
|
|
|
|
rearrangeLayer(windows, &workArea, LayerSurfaceV1Interface::BottomLayer, true);
|
|
|
|
rearrangeLayer(windows, &workArea, LayerSurfaceV1Interface::BackgroundLayer, true);
|
2020-08-07 19:01:42 +00:00
|
|
|
|
2022-04-23 08:33:23 +00:00
|
|
|
rearrangeLayer(windows, &workArea, LayerSurfaceV1Interface::OverlayLayer, false);
|
|
|
|
rearrangeLayer(windows, &workArea, LayerSurfaceV1Interface::TopLayer, false);
|
|
|
|
rearrangeLayer(windows, &workArea, LayerSurfaceV1Interface::BottomLayer, false);
|
|
|
|
rearrangeLayer(windows, &workArea, LayerSurfaceV1Interface::BackgroundLayer, false);
|
2020-08-07 19:01:42 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void LayerShellV1Integration::rearrange()
|
|
|
|
{
|
|
|
|
m_rearrangeTimer->stop();
|
|
|
|
|
2022-04-14 12:33:28 +00:00
|
|
|
const QVector<Output *> outputs = kwinApp()->platform()->enabledOutputs();
|
|
|
|
for (Output *output : outputs) {
|
2020-08-07 19:01:42 +00:00
|
|
|
rearrangeOutput(output);
|
|
|
|
}
|
|
|
|
|
Prevent layershell from crashing when rearranging when we shouldn't
Here's the backtrace that prompted the MR:
```
0 QVector<KWin::VirtualDesktop*>::count() const (this=0x10) at
/home/apol/devel/kde5/include/QtCore/qvector.h:241
1 KWin::VirtualDesktopManager::count() const (this=0x0) at
/home/apol/devel/frameworks/kwin/src/virtualdesktops.h:687
2 KWin::Workspace::updateClientArea(bool) (this=0x0, force=false) at
/home/apol/devel/frameworks/kwin/src/workspace.cpp:2089
3 0x00007fef12a180b3 in KWin::LayerShellV1Integration::rearrange()
(this=<optimized out>) at
/home/apol/devel/frameworks/kwin/src/layershellv1integration.cpp:208
4 0x00007fef13094806 in QtPrivate::QSlotObjectBase::call(QObject*,
void**) (a=0x7ffcf9674f70, r=0x5569d2981a40, this=0x5569d2981e50) at
../../include/QtCore/../../../../../devel/frameworks/qt5/qtbase/src/corelib/kernel/qobjectdefs_impl.h:398
5 doActivate<false>(QObject*, int, void**) (sender=0x5569d2981dc0,
signal_index=3, argv=argv@entry=0x7ffcf9674f70) at
/home/apol/devel/frameworks/qt5/qtbase/src/corelib/kernel/qobject.cpp:3886
6 0x00007fef1308db60 in QMetaObject::activate(QObject*, QMetaObject
const*, int, void**) (sender=<optimized out>, m=m@entry=0x7fef1332d280
<QTimer::staticMetaObject>,
local_signal_index=local_signal_index@entry=0,
argv=argv@entry=0x7ffcf9674f70) at
/home/apol/devel/frameworks/qt5/qtbase/src/corelib/kernel/qobject.cpp:3946
7 0x00007fef1309871a in QTimer::timeout(QTimer::QPrivateSignal)
(this=<optimized out>, _t1=...) at .moc/moc_qtimer.cpp:205
```
2021-02-17 17:02:17 +00:00
|
|
|
if (workspace()) {
|
|
|
|
workspace()->updateClientArea();
|
|
|
|
}
|
2020-08-07 19:01:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void LayerShellV1Integration::scheduleRearrange()
|
|
|
|
{
|
|
|
|
m_rearrangeTimer->start();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace KWin
|