[wayland] XdgDecoration Support

Summary:
Does something similar to our existing ServerDecoration, but based
around XDG Shell patterns and with a few subtle differneces.

We'll probably still need both in kwin for the forseeable future as GTK3
won't ever change from using the KDE Server Decoration.

Test Plan:
Relevant unit test. It's a bit simpler as spec states
toolkits must follow what the compositor configures if they
bind the interface.

Modified plasma-integration to remove ServerIntegration
(as Qt5.12 has native support) all my windows look and act the same.

Reviewers: #kwin, zzag

Reviewed By: #kwin, zzag

Subscribers: kwin

Tags: #kwin

Differential Revision: https://phabricator.kde.org/D17756
icc-effect-5.17.5
David Edmundson 2019-01-01 17:37:18 +00:00
parent 48d13379c3
commit ac45977e9e
7 changed files with 115 additions and 5 deletions

View File

@ -46,6 +46,7 @@ class Shell;
class ShellSurface;
class ShmPool;
class Surface;
class XdgDecorationManager;
}
}
@ -89,7 +90,8 @@ enum class AdditionalWaylandInterface {
PointerConstraints = 1 << 4,
IdleInhibition = 1 << 5,
AppMenu = 1 << 6,
ShadowManager = 1 << 7
ShadowManager = 1 << 7,
XdgDecoration = 1 << 8,
};
Q_DECLARE_FLAGS(AdditionalWaylandInterfaces, AdditionalWaylandInterface)
/**
@ -120,7 +122,7 @@ KWayland::Client::PlasmaWindowManagement *waylandWindowManagement();
KWayland::Client::PointerConstraints *waylandPointerConstraints();
KWayland::Client::IdleInhibitManager *waylandIdleInhibitManager();
KWayland::Client::AppMenuManager *waylandAppMenuManager();
KWayland::Client::XdgDecorationManager *xdgDecorationManager();
bool waitForWaylandPointer();
bool waitForWaylandTouch();

View File

@ -36,12 +36,13 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <KWayland/Client/server_decoration.h>
#include <KWayland/Client/surface.h>
#include <KWayland/Client/xdgshell.h>
#include <KWayland/Client/xdgdecoration.h>
#include <KWayland/Client/appmenu.h>
#include <KWayland/Server/clientconnection.h>
#include <KWayland/Server/display.h>
#include <KWayland/Server/shell_interface.h>
#include <KWayland/Server/xdgdecoration_interface.h>
// system
#include <sys/types.h>
@ -96,6 +97,8 @@ private Q_SLOTS:
void testSendClientWithTransientToDesktop();
void testMinimizeWindowWithTransients_data();
void testMinimizeWindowWithTransients();
void testXdgDecoration_data();
void testXdgDecoration();
};
void TestShellClient::initTestCase()
@ -122,6 +125,7 @@ void TestShellClient::initTestCase()
void TestShellClient::init()
{
QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Decoration |
Test::AdditionalWaylandInterface::XdgDecoration |
Test::AdditionalWaylandInterface::AppMenu));
screens()->setCurrent(0);
@ -1196,5 +1200,44 @@ void TestShellClient::testMinimizeWindowWithTransients()
QVERIFY(!transient->isMinimized());
}
void TestShellClient::testXdgDecoration_data()
{
QTest::addColumn<KWayland::Client::XdgDecoration::Mode>("requestedMode");
QTest::addColumn<KWayland::Client::XdgDecoration::Mode>("expectedMode");
QTest::newRow("client side requested") << XdgDecoration::Mode::ClientSide << XdgDecoration::Mode::ClientSide;
QTest::newRow("server side requested") << XdgDecoration::Mode::ServerSide << XdgDecoration::Mode::ServerSide;
}
void TestShellClient::testXdgDecoration()
{
QScopedPointer<Surface> surface(Test::createSurface());
QScopedPointer<XdgShellSurface> shellSurface(Test::createXdgShellStableSurface(surface.data()));
QScopedPointer<XdgDecoration> deco(Test::xdgDecorationManager()->getToplevelDecoration(shellSurface.data()));
QSignalSpy decorationConfiguredSpy(deco.data(), &XdgDecoration::modeChanged);
QSignalSpy configureRequestedSpy(shellSurface.data(), &XdgShellSurface::configureRequested);
QFETCH(KWayland::Client::XdgDecoration::Mode, requestedMode);
QFETCH(KWayland::Client::XdgDecoration::Mode, expectedMode);
//request a mode
deco->setMode(requestedMode);
//kwin will send a configure
decorationConfiguredSpy.wait();
configureRequestedSpy.wait();
QCOMPARE(decorationConfiguredSpy.count(), 1);
QCOMPARE(decorationConfiguredSpy.first()[0].value<KWayland::Client::XdgDecoration::Mode>(), expectedMode);
QVERIFY(configureRequestedSpy.count() > 0);
shellSurface->ackConfigure(configureRequestedSpy.last()[2].toInt());
auto c = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue);
QCOMPARE(c->userCanSetNoBorder(), expectedMode == XdgDecoration::Mode::ServerSide);
QCOMPARE(c->isDecorated(), expectedMode == XdgDecoration::Mode::ServerSide);
}
WAYLANDTEST_MAIN(TestShellClient)
#include "shell_client_test.moc"

View File

@ -39,6 +39,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <KWayland/Client/surface.h>
#include <KWayland/Client/appmenu.h>
#include <KWayland/Client/xdgshell.h>
#include <KWayland/Client/xdgdecoration.h>
#include <KWayland/Server/display.h>
//screenlocker
@ -78,6 +79,7 @@ static struct {
QVector<Output*> outputs;
IdleInhibitManager *idleInhibit = nullptr;
AppMenuManager *appMenu = nullptr;
XdgDecorationManager *xdgDecoration = nullptr;
} s_waylandConnection;
bool setupWaylandConnection(AdditionalWaylandInterfaces flags)
@ -218,6 +220,12 @@ bool setupWaylandConnection(AdditionalWaylandInterfaces flags)
return false;
}
}
if (flags.testFlag(AdditionalWaylandInterface::XdgDecoration)) {
s_waylandConnection.xdgDecoration = registry->createXdgDecorationManager(registry->interface(Registry::Interface::XdgDecorationUnstableV1).name, registry->interface(Registry::Interface::XdgDecorationUnstableV1).version);
if (!s_waylandConnection.xdgDecoration->isValid()) {
return false;
}
}
return true;
}
@ -258,6 +266,8 @@ void destroyWaylandConnection()
s_waylandConnection.registry = nullptr;
delete s_waylandConnection.appMenu;
s_waylandConnection.appMenu = nullptr;
delete s_waylandConnection.xdgDecoration;
s_waylandConnection.xdgDecoration = nullptr;
if (s_waylandConnection.thread) {
QSignalSpy spy(s_waylandConnection.connection, &QObject::destroyed);
s_waylandConnection.connection->deleteLater();
@ -332,6 +342,12 @@ AppMenuManager* waylandAppMenuManager()
return s_waylandConnection.appMenu;
}
XdgDecorationManager *xdgDecorationManager()
{
return s_waylandConnection.xdgDecoration;
}
bool waitForWaylandPointer()
{
if (!s_waylandConnection.seat) {

View File

@ -48,6 +48,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <KWayland/Server/plasmawindowmanagement_interface.h>
#include <KWayland/Server/appmenu_interface.h>
#include <KWayland/Server/server_decoration_palette_interface.h>
#include <KWayland/Server/xdgdecoration_interface.h>
#include <KDesktopFile>
@ -579,6 +580,11 @@ void ShellClient::updateDecoration(bool check_workspace_pos, bool force)
if (m_serverDecoration && isDecorated()) {
m_serverDecoration->setMode(KWayland::Server::ServerSideDecorationManagerInterface::Mode::Server);
}
if (m_xdgDecoration) {
auto mode = isDecorated() ? XdgDecorationInterface::Mode::ServerSide: XdgDecorationInterface::Mode::ClientSide;
m_xdgDecoration->configure(mode);
m_xdgShellSurface->configure(xdgSurfaceStates(), m_requestedClientSize);
}
getShadow();
if (check_workspace_pos)
checkWorkspacePosition(oldgeom, -2, oldClientGeom);
@ -932,6 +938,9 @@ bool ShellClient::noBorder() const
return m_userNoBorder || isFullScreen();
}
}
if (m_xdgDecoration && m_xdgDecoration->requestedMode() != XdgDecorationInterface::Mode::ClientSide) {
return m_userNoBorder || isFullScreen();
}
return true;
}
@ -1056,6 +1065,9 @@ bool ShellClient::userCanSetNoBorder() const
if (m_serverDecoration && m_serverDecoration->mode() == ServerSideDecorationManagerInterface::Mode::Server) {
return !isFullScreen() && !isShade() && !tabGroup();
}
if (m_xdgDecoration && m_xdgDecoration->requestedMode() != XdgDecorationInterface::Mode::ClientSide) {
return !isFullScreen() && !isShade() && !tabGroup();
}
if (m_internal) {
return !m_internalWindowFlags.testFlag(Qt::FramelessWindowHint) || m_internalWindowFlags.testFlag(Qt::Popup);
}
@ -1194,7 +1206,7 @@ void ShellClient::requestGeometry(const QRect &rect)
configureRequest.maximizeMode = m_requestedMaximizeMode;
const QSize size = rect.size() - QSize(borderLeft() + borderRight(), borderTop() + borderBottom());
m_requestedClientSize = QSize(0, 0);
m_requestedClientSize = size;
if (m_shellSurface) {
m_shellSurface->requestSize(size);
@ -1274,7 +1286,7 @@ void ShellClient::resizeWithChecks(int w, int h, ForceGeometry_t force)
void ShellClient::unmap()
{
m_unmapped = true;
m_requestedClientSize = QSize();
m_requestedClientSize = QSize(0, 0);
destroyWindowManagementInterface();
if (Workspace::self()) {
addWorkspaceRepaint(visibleRect());
@ -1806,6 +1818,29 @@ void ShellClient::installServerSideDecoration(KWayland::Server::ServerSideDecora
);
}
void ShellClient::installXdgDecoration(XdgDecorationInterface *deco)
{
Q_ASSERT(m_xdgShellSurface);
m_xdgDecoration = deco;
connect(m_xdgDecoration, &QObject::destroyed, this,
[this] {
m_xdgDecoration = nullptr;
if (m_closing || !Workspace::self()) {
return;
}
updateDecoration(true);
}
);
connect(m_xdgDecoration, &XdgDecorationInterface::modeRequested, this,
[this] () {
//force is true as we must send a new configure response
updateDecoration(false, true);
});
}
bool ShellClient::shouldExposeToWindowManagement()
{
if (isInternal()) {

View File

@ -34,6 +34,7 @@ class ServerSideDecorationPaletteInterface;
class AppMenuInterface;
class PlasmaShellSurfaceInterface;
class QtExtendedSurfaceInterface;
class XdgDecorationInterface;
}
}
@ -142,6 +143,7 @@ public:
void installServerSideDecoration(KWayland::Server::ServerSideDecorationInterface *decoration);
void installAppMenu(KWayland::Server::AppMenuInterface *appmenu);
void installPalette(KWayland::Server::ServerSideDecorationPaletteInterface *palette);
void installXdgDecoration(KWayland::Server::XdgDecorationInterface *decoration);
bool isInitialPositionSet() const override;
@ -257,6 +259,7 @@ private:
QPointer<KWayland::Server::AppMenuInterface> m_appMenuInterface;
QPointer<KWayland::Server::ServerSideDecorationPaletteInterface> m_paletteInterface;
KWayland::Server::ServerSideDecorationInterface *m_serverDecoration = nullptr;
KWayland::Server::XdgDecorationInterface *m_xdgDecoration = nullptr;
bool m_userNoBorder = false;
bool m_fullScreen = false;
bool m_transient = false;

View File

@ -57,6 +57,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <KWayland/Server/shell_interface.h>
#include <KWayland/Server/outputmanagement_interface.h>
#include <KWayland/Server/outputconfiguration_interface.h>
#include <KWayland/Server/xdgdecoration_interface.h>
#include <KWayland/Server/xdgshell_interface.h>
#include <KWayland/Server/xdgforeign_interface.h>
#include <KWayland/Server/xdgoutput_interface.h>
@ -235,6 +236,14 @@ bool WaylandServer::init(const QByteArray &socketName, InitalizationFlags flags)
connect(m_xdgShell, &XdgShellInterface::surfaceCreated, this, &WaylandServer::createSurface<XdgShellSurfaceInterface>);
connect(m_xdgShell, &XdgShellInterface::xdgPopupCreated, this, &WaylandServer::createSurface<XdgShellPopupInterface>);
m_xdgDecorationManager = m_display->createXdgDecorationManager(m_xdgShell, m_display);
m_xdgDecorationManager->create();
connect(m_xdgDecorationManager, &XdgDecorationManagerInterface::xdgDecorationInterfaceCreated, this, [this] (XdgDecorationInterface *deco) {
if (ShellClient *client = findClient(deco->surface()->surface())) {
client->installXdgDecoration(deco);
}
});
m_display->createShm();
m_seat = m_display->createSeat(m_display);
m_seat->create();

View File

@ -59,6 +59,7 @@ class PlasmaWindowManagementInterface;
class QtSurfaceExtensionInterface;
class OutputManagementInterface;
class OutputConfigurationInterface;
class XdgDecorationManagerInterface;
class XdgShellInterface;
class XdgForeignInterface;
class XdgOutputManagerInterface;
@ -246,6 +247,7 @@ private:
KWayland::Server::ServerSideDecorationPaletteManagerInterface *m_paletteManager = nullptr;
KWayland::Server::IdleInterface *m_idle = nullptr;
KWayland::Server::XdgOutputManagerInterface *m_xdgOutputManager = nullptr;
KWayland::Server::XdgDecorationManagerInterface *m_xdgDecorationManager = nullptr;
struct {
KWayland::Server::ClientConnection *client = nullptr;
QMetaObject::Connection destroyConnection;