wayland: Implement kde-screen-edge-v1

It's needed to port the plasma panel to the layer shell protocol.
master
Vlad Zahorodnii 2 years ago
parent 643a5f53e5
commit 83261fc82a

@ -17,6 +17,7 @@ qt6_generate_wayland_protocol_client_sources(KWinIntegrationTestFramework
${WaylandProtocols_DATADIR}/staging/fractional-scale/fractional-scale-v1.xml
${PLASMA_WAYLAND_PROTOCOLS_DIR}/kde-output-device-v2.xml
${PLASMA_WAYLAND_PROTOCOLS_DIR}/kde-output-management-v2.xml
${PLASMA_WAYLAND_PROTOCOLS_DIR}/kde-screen-edge-v1.xml
${PLASMA_WAYLAND_PROTOCOLS_DIR}/zkde-screencast-unstable-v1.xml
)

@ -24,6 +24,7 @@
#include "qwayland-input-method-unstable-v1.h"
#include "qwayland-kde-output-device-v2.h"
#include "qwayland-kde-output-management-v2.h"
#include "qwayland-kde-screen-edge-v1.h"
#include "qwayland-text-input-unstable-v3.h"
#include "qwayland-wlr-layer-shell-unstable-v1.h"
#include "qwayland-xdg-decoration-unstable-v1.h"
@ -487,6 +488,21 @@ private:
int m_preferredScale = 120;
};
class ScreenEdgeManagerV1 : public QObject, public QtWayland::kde_screen_edge_manager_v1
{
Q_OBJECT
public:
~ScreenEdgeManagerV1() override;
};
class AutoHideScreenEdgeV1 : public QObject, public QtWayland::kde_auto_hide_screen_edge_v1
{
Q_OBJECT
public:
AutoHideScreenEdgeV1(ScreenEdgeManagerV1 *manager, KWayland::Client::Surface *surface, uint32_t border);
~AutoHideScreenEdgeV1() override;
};
enum class AdditionalWaylandInterface {
Seat = 1 << 0,
Decoration = 1 << 1,
@ -505,6 +521,7 @@ enum class AdditionalWaylandInterface {
OutputDeviceV2 = 1 << 14,
FractionalScaleManagerV1 = 1 << 15,
ScreencastingV1 = 1 << 16,
ScreenEdgeV1 = 1 << 17,
};
Q_DECLARE_FLAGS(AdditionalWaylandInterfaces, AdditionalWaylandInterface)
@ -650,6 +667,7 @@ XdgPopup *createXdgPopupSurface(KWayland::Client::Surface *surface, XdgSurface *
XdgToplevelDecorationV1 *createXdgToplevelDecorationV1(XdgToplevel *toplevel, QObject *parent = nullptr);
IdleInhibitorV1 *createIdleInhibitorV1(KWayland::Client::Surface *surface);
AutoHideScreenEdgeV1 *createAutoHideScreenEdgeV1(KWayland::Client::Surface *surface, uint32_t border);
/**
* Creates a shared memory buffer of @p size in @p color and attaches it to the @p surface.

@ -51,6 +51,7 @@ private Q_SLOTS:
void testActivate_data();
void testActivate();
void testUnmap();
void testScreenEdge();
};
void LayerShellV1WindowTest::initTestCase()
@ -72,7 +73,7 @@ void LayerShellV1WindowTest::initTestCase()
void LayerShellV1WindowTest::init()
{
QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::LayerShellV1));
QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::LayerShellV1 | Test::AdditionalWaylandInterface::ScreenEdgeV1));
workspace()->setActiveOutput(QPoint(640, 512));
input()->pointer()->warp(QPoint(640, 512));
@ -610,6 +611,61 @@ void LayerShellV1WindowTest::testUnmap()
QVERIFY(Test::waitForWindowClosed(window));
}
void LayerShellV1WindowTest::testScreenEdge()
{
// Create a layer shell surface.
std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
std::unique_ptr<Test::LayerSurfaceV1> shellSurface(Test::createLayerSurfaceV1(surface.get(), QStringLiteral("test")));
std::unique_ptr<Test::AutoHideScreenEdgeV1> screenEdge(Test::createAutoHideScreenEdgeV1(surface.get(), Test::ScreenEdgeManagerV1::border_bottom));
// Set the initial state of the layer surface.
shellSurface->set_layer(Test::LayerShellV1::layer_top);
shellSurface->set_anchor(Test::LayerSurfaceV1::anchor_bottom);
shellSurface->set_size(100, 50);
surface->commit(KWayland::Client::Surface::CommitFlag::None);
// Wait for the compositor to position the surface.
QSignalSpy configureRequestedSpy(shellSurface.get(), &Test::LayerSurfaceV1::configureRequested);
QVERIFY(configureRequestedSpy.wait());
const QSize requestedSize = configureRequestedSpy.last().at(1).toSize();
// Map the layer surface.
shellSurface->ack_configure(configureRequestedSpy.last().at(0).toUInt());
Window *window = Test::renderAndWaitForShown(surface.get(), requestedSize, Qt::red);
QVERIFY(window);
QVERIFY(!window->isActive());
// The layer surface will be hidden and shown when the screen edge is activated or deactivated.
QSignalSpy windowShowSpy(window, &Window::windowShown);
QSignalSpy windowHiddenSpy(window, &Window::windowHidden);
screenEdge->activate();
QVERIFY(windowHiddenSpy.wait());
QVERIFY(!window->isShown());
screenEdge->deactivate();
QVERIFY(windowShowSpy.wait());
QVERIFY(window->isShown());
// The layer surface will be shown when the screen edge is triggered.
screenEdge->activate();
QVERIFY(windowHiddenSpy.wait());
QVERIFY(!window->isShown());
Test::pointerMotion(QPointF(640, 1023), 0);
Test::pointerMotion(QPointF(640, 512), 1);
QVERIFY(windowShowSpy.wait());
QVERIFY(window->isShown());
// The layer surface will be shown when the screen edge is destroyed.
screenEdge->activate();
QVERIFY(windowHiddenSpy.wait());
QVERIFY(!window->isShown());
screenEdge.reset();
QVERIFY(windowShowSpy.wait());
QVERIFY(window->isShown());
}
} // namespace KWin
WAYLANDTEST_MAIN(KWin::LayerShellV1WindowTest)

@ -227,6 +227,21 @@ IdleInhibitorV1::~IdleInhibitorV1()
destroy();
}
ScreenEdgeManagerV1::~ScreenEdgeManagerV1()
{
destroy();
}
AutoHideScreenEdgeV1::AutoHideScreenEdgeV1(ScreenEdgeManagerV1 *manager, KWayland::Client::Surface *surface, uint32_t border)
: QtWayland::kde_auto_hide_screen_edge_v1(manager->get_auto_hide_screen_edge(border, *surface))
{
}
AutoHideScreenEdgeV1::~AutoHideScreenEdgeV1()
{
destroy();
}
static struct
{
KWayland::Client::ConnectionThread *connection = nullptr;
@ -257,6 +272,7 @@ static struct
TextInputManagerV3 *textInputManagerV3 = nullptr;
FractionalScaleManagerV1 *fractionalScaleManagerV1 = nullptr;
ScreencastingV1 *screencastingV1 = nullptr;
ScreenEdgeManagerV1 *screenEdgeManagerV1 = nullptr;
} s_waylandConnection;
MockInputMethod *inputMethod()
@ -435,6 +451,13 @@ bool setupWaylandConnection(AdditionalWaylandInterfaces flags)
return;
}
}
if (flags & AdditionalWaylandInterface::ScreenEdgeV1) {
if (interface == kde_screen_edge_manager_v1_interface.name) {
s_waylandConnection.screenEdgeManagerV1 = new ScreenEdgeManagerV1();
s_waylandConnection.screenEdgeManagerV1->init(*registry, name, version);
return;
}
}
});
QSignalSpy allAnnounced(registry, &KWayland::Client::Registry::interfacesAnnounced);
@ -561,6 +584,10 @@ void destroyWaylandConnection()
s_waylandConnection.outputManagementV2 = nullptr;
delete s_waylandConnection.fractionalScaleManagerV1;
s_waylandConnection.fractionalScaleManagerV1 = nullptr;
delete s_waylandConnection.screencastingV1;
s_waylandConnection.screencastingV1 = nullptr;
delete s_waylandConnection.screenEdgeManagerV1;
s_waylandConnection.screenEdgeManagerV1 = nullptr;
delete s_waylandConnection.queue; // Must be destroyed last
s_waylandConnection.queue = nullptr;
@ -969,6 +996,17 @@ IdleInhibitorV1 *createIdleInhibitorV1(KWayland::Client::Surface *surface)
return new IdleInhibitorV1(manager, surface);
}
AutoHideScreenEdgeV1 *createAutoHideScreenEdgeV1(KWayland::Client::Surface *surface, uint32_t border)
{
ScreenEdgeManagerV1 *manager = s_waylandConnection.screenEdgeManagerV1;
if (!manager) {
qWarning() << "Could not create an kde_auto_hide_screen_edge_v1 because kde_screen_edge_manager_v1 global is not bound";
return nullptr;
}
return new AutoHideScreenEdgeV1(manager, surface, border);
}
bool waitForWindowClosed(Window *window)
{
QSignalSpy closedSpy(window, &Window::closed);

@ -7,8 +7,10 @@
#include "layershellv1window.h"
#include "core/output.h"
#include "layershellv1integration.h"
#include "screenedge.h"
#include "wayland/layershell_v1_interface.h"
#include "wayland/output_interface.h"
#include "wayland/screenedge_v1_interface.h"
#include "wayland/surface_interface.h"
#include "wayland_server.h"
#include "workspace.h"
@ -173,6 +175,9 @@ bool LayerShellV1Window::hasStrut() const
void LayerShellV1Window::destroyWindow()
{
if (m_screenEdge) {
m_screenEdge->disconnect(this);
}
m_shellSurface->disconnect(this);
m_shellSurface->surface()->disconnect(this);
@ -290,4 +295,53 @@ void LayerShellV1Window::setVirtualKeyboardGeometry(const QRectF &geo)
scheduleRearrange();
}
void LayerShellV1Window::showOnScreenEdge()
{
// ShowOnScreenEdge can be called by an Edge, and hideClient could destroy the Edge
// Use the singleshot to avoid use-after-free
QTimer::singleShot(0, this, &LayerShellV1Window::deactivateScreenEdge);
}
void LayerShellV1Window::installAutoHideScreenEdgeV1(KWaylandServer::AutoHideScreenEdgeV1Interface *edge)
{
m_screenEdge = edge;
connect(edge, &KWaylandServer::AutoHideScreenEdgeV1Interface::destroyed,
this, &LayerShellV1Window::deactivateScreenEdge);
connect(edge, &KWaylandServer::AutoHideScreenEdgeV1Interface::activateRequested,
this, &LayerShellV1Window::activateScreenEdge);
connect(edge, &KWaylandServer::AutoHideScreenEdgeV1Interface::deactivateRequested,
this, &LayerShellV1Window::deactivateScreenEdge);
connect(this, &LayerShellV1Window::frameGeometryChanged, edge, [this]() {
if (m_screenEdgeActive) {
reserveScreenEdge();
}
});
}
void LayerShellV1Window::reserveScreenEdge()
{
hideClient();
workspace()->screenEdges()->reserve(this, m_screenEdge->border());
}
void LayerShellV1Window::unreserveScreenEdge()
{
showClient();
workspace()->screenEdges()->reserve(this, ElectricNone);
}
void LayerShellV1Window::activateScreenEdge()
{
m_screenEdgeActive = true;
reserveScreenEdge();
}
void LayerShellV1Window::deactivateScreenEdge()
{
m_screenEdgeActive = false;
unreserveScreenEdge();
}
} // namespace KWin

@ -10,6 +10,7 @@
namespace KWaylandServer
{
class AutoHideScreenEdgeV1Interface;
class LayerSurfaceV1Interface;
}
@ -44,6 +45,9 @@ public:
void destroyWindow() override;
void closeWindow() override;
void setVirtualKeyboardGeometry(const QRectF &geo) override;
void showOnScreenEdge() override;
void installAutoHideScreenEdgeV1(KWaylandServer::AutoHideScreenEdgeV1Interface *edge);
protected:
Layer belongsToLayer() const override;
@ -58,10 +62,16 @@ private:
void handleOutputEnabledChanged();
void handleOutputDestroyed();
void scheduleRearrange();
void activateScreenEdge();
void deactivateScreenEdge();
void reserveScreenEdge();
void unreserveScreenEdge();
Output *m_desiredOutput;
LayerShellV1Integration *m_integration;
KWaylandServer::LayerSurfaceV1Interface *m_shellSurface;
QPointer<KWaylandServer::AutoHideScreenEdgeV1Interface> m_screenEdge;
bool m_screenEdgeActive = false;
NET::WindowType m_windowType;
};

@ -208,6 +208,10 @@ ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
PROTOCOL ${PROJECT_SOURCE_DIR}/src/wayland/protocols/wayland-drm.xml
BASENAME drm
)
ecm_add_qtwayland_server_protocol_kde(WaylandProtocols_xml
PROTOCOL ${PLASMA_WAYLAND_PROTOCOLS_DIR}/kde-screen-edge-v1.xml
BASENAME kde-screen-edge-v1
)
target_sources(kwin PRIVATE
abstract_data_source.cpp
@ -260,6 +264,7 @@ target_sources(kwin PRIVATE
region_interface.cpp
relativepointer_v1_interface.cpp
screencast_v1_interface.cpp
screenedge_v1_interface.cpp
seat_interface.cpp
server_decoration_interface.cpp
server_decoration_palette_interface.cpp

@ -0,0 +1,149 @@
/*
SPDX-FileCopyrightText: 2023 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "wayland/screenedge_v1_interface.h"
#include "wayland/display.h"
#include "wayland/surface_interface.h"
#include "wayland/surfacerole_p.h"
#include "qwayland-server-kde-screen-edge-v1.h"
using namespace KWin;
namespace KWaylandServer
{
static const int s_version = 1;
class ScreenEdgeManagerV1InterfacePrivate : public QtWaylandServer::kde_screen_edge_manager_v1
{
public:
ScreenEdgeManagerV1InterfacePrivate(ScreenEdgeManagerV1Interface *q, Display *display);
ScreenEdgeManagerV1Interface *q;
protected:
void kde_screen_edge_manager_v1_destroy(Resource *resource) override;
void kde_screen_edge_manager_v1_get_auto_hide_screen_edge(Resource *resource, uint32_t id, uint32_t border, struct ::wl_resource *surface) override;
};
ScreenEdgeManagerV1InterfacePrivate::ScreenEdgeManagerV1InterfacePrivate(ScreenEdgeManagerV1Interface *q, Display *display)
: QtWaylandServer::kde_screen_edge_manager_v1(*display, s_version)
, q(q)
{
}
void ScreenEdgeManagerV1InterfacePrivate::kde_screen_edge_manager_v1_destroy(Resource *resource)
{
wl_resource_destroy(resource->handle);
}
void ScreenEdgeManagerV1InterfacePrivate::kde_screen_edge_manager_v1_get_auto_hide_screen_edge(Resource *resource, uint32_t id, uint32_t border, struct ::wl_resource *surface_resource)
{
ElectricBorder electricBorder;
switch (border) {
case border_top:
electricBorder = ElectricTop;
break;
case border_bottom:
electricBorder = ElectricBottom;
break;
case border_left:
electricBorder = ElectricLeft;
break;
case border_right:
electricBorder = ElectricRight;
break;
default:
wl_resource_post_error(resource->handle, error_invalid_border, "invalid border");
return;
}
SurfaceInterface *surface = SurfaceInterface::get(surface_resource);
SurfaceRole *role = SurfaceRole::get(surface);
if (!role || role->name() != "layer_surface_v1") {
wl_resource_post_error(resource->handle, error_invalid_role, "surface must have layer_surface role");
return;
}
wl_resource *edgeResource = wl_resource_create(resource->client(), &kde_auto_hide_screen_edge_v1_interface, resource->version(), id);
auto edge = new AutoHideScreenEdgeV1Interface(surface, electricBorder, edgeResource);
Q_EMIT q->edgeRequested(edge);
}
ScreenEdgeManagerV1Interface::ScreenEdgeManagerV1Interface(Display *display, QObject *parent)
: d(new ScreenEdgeManagerV1InterfacePrivate(this, display))
{
}
ScreenEdgeManagerV1Interface::~ScreenEdgeManagerV1Interface()
{
}
class AutoHideScreenEdgeV1InterfacePrivate : public QtWaylandServer::kde_auto_hide_screen_edge_v1
{
public:
AutoHideScreenEdgeV1InterfacePrivate(AutoHideScreenEdgeV1Interface *q, SurfaceInterface *surface, ElectricBorder border, wl_resource *resource);
AutoHideScreenEdgeV1Interface *q;
QPointer<SurfaceInterface> surface;
ElectricBorder border;
protected:
void kde_auto_hide_screen_edge_v1_destroy_resource(Resource *resource) override;
void kde_auto_hide_screen_edge_v1_destroy(Resource *resource) override;
void kde_auto_hide_screen_edge_v1_deactivate(Resource *resource) override;
void kde_auto_hide_screen_edge_v1_activate(Resource *resource) override;
};
AutoHideScreenEdgeV1InterfacePrivate::AutoHideScreenEdgeV1InterfacePrivate(AutoHideScreenEdgeV1Interface *q, SurfaceInterface *surface, ElectricBorder border, wl_resource *resource)
: QtWaylandServer::kde_auto_hide_screen_edge_v1(resource)
, q(q)
, surface(surface)
, border(border)
{
}
void AutoHideScreenEdgeV1InterfacePrivate::kde_auto_hide_screen_edge_v1_destroy_resource(Resource *resource)
{
delete q;
}
void AutoHideScreenEdgeV1InterfacePrivate::kde_auto_hide_screen_edge_v1_destroy(Resource *resource)
{
wl_resource_destroy(resource->handle);
}
void AutoHideScreenEdgeV1InterfacePrivate::kde_auto_hide_screen_edge_v1_deactivate(Resource *resource)
{
Q_EMIT q->deactivateRequested();
}
void AutoHideScreenEdgeV1InterfacePrivate::kde_auto_hide_screen_edge_v1_activate(Resource *resource)
{
Q_EMIT q->activateRequested();
}
AutoHideScreenEdgeV1Interface::AutoHideScreenEdgeV1Interface(SurfaceInterface *surface, ElectricBorder border, wl_resource *resource)
: d(new AutoHideScreenEdgeV1InterfacePrivate(this, surface, border, resource))
{
}
AutoHideScreenEdgeV1Interface::~AutoHideScreenEdgeV1Interface()
{
}
SurfaceInterface *AutoHideScreenEdgeV1Interface::surface() const
{
return d->surface;
}
ElectricBorder AutoHideScreenEdgeV1Interface::border() const
{
return d->border;
}
} // namespace KWaylandServer

@ -0,0 +1,59 @@
/*
SPDX-FileCopyrightText: 2023 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#pragma once
#include "kwinglobals.h"
#include <QObject>
#include <memory>
struct wl_resource;
namespace KWaylandServer
{
class Display;
class ScreenEdgeManagerV1InterfacePrivate;
class AutoHideScreenEdgeV1Interface;
class AutoHideScreenEdgeV1InterfacePrivate;
class SurfaceInterface;
class KWIN_EXPORT ScreenEdgeManagerV1Interface : public QObject
{
Q_OBJECT
public:
explicit ScreenEdgeManagerV1Interface(Display *display, QObject *parent = nullptr);
~ScreenEdgeManagerV1Interface() override;
Q_SIGNALS:
void edgeRequested(AutoHideScreenEdgeV1Interface *edge);
private:
std::unique_ptr<ScreenEdgeManagerV1InterfacePrivate> d;
};
class KWIN_EXPORT AutoHideScreenEdgeV1Interface : public QObject
{
Q_OBJECT
public:
AutoHideScreenEdgeV1Interface(SurfaceInterface *surface, KWin::ElectricBorder border, wl_resource *resource);
~AutoHideScreenEdgeV1Interface() override;
SurfaceInterface *surface() const;
KWin::ElectricBorder border() const;
Q_SIGNALS:
void deactivateRequested();
void activateRequested();
private:
std::unique_ptr<AutoHideScreenEdgeV1InterfacePrivate> d;
};
} // namespace KWaylandServer

@ -16,6 +16,7 @@
#include "idle_inhibition.h"
#include "inputpanelv1integration.h"
#include "layershellv1integration.h"
#include "layershellv1window.h"
#include "main.h"
#include "utils/serviceutils.h"
#include "virtualdesktops.h"
@ -49,6 +50,7 @@
#include "wayland/pointergestures_v1_interface.h"
#include "wayland/primaryselectiondevicemanager_v1_interface.h"
#include "wayland/relativepointer_v1_interface.h"
#include "wayland/screenedge_v1_interface.h"
#include "wayland/seat_interface.h"
#include "wayland/server_decoration_interface.h"
#include "wayland/server_decoration_palette_interface.h"
@ -510,6 +512,13 @@ bool WaylandServer::init(InitializationFlags flags)
m_contentTypeManager = new KWaylandServer::ContentTypeManagerV1Interface(m_display, m_display);
m_tearingControlInterface = new KWaylandServer::TearingControlManagerV1Interface(m_display, m_display);
auto screenEdgeManager = new KWaylandServer::ScreenEdgeManagerV1Interface(m_display, m_display);
connect(screenEdgeManager, &KWaylandServer::ScreenEdgeManagerV1Interface::edgeRequested, this, [this](KWaylandServer::AutoHideScreenEdgeV1Interface *edge) {
if (auto window = qobject_cast<LayerShellV1Window *>(findWindow(edge->surface()))) {
window->installAutoHideScreenEdgeV1(edge);
}
});
return true;
}

Loading…
Cancel
Save