[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
This commit is contained in:
parent
48d13379c3
commit
ac45977e9e
7 changed files with 115 additions and 5 deletions
|
@ -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();
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue