diff --git a/src/wayland/CMakeLists.txt b/src/wayland/CMakeLists.txt
index 092f725aaa..bfb268b484 100644
--- a/src/wayland/CMakeLists.txt
+++ b/src/wayland/CMakeLists.txt
@@ -37,6 +37,8 @@ set(SERVER_LIB_SRCS
textinput_interface.cpp
textinput_interface_v0.cpp
textinput_interface_v2.cpp
+ xdgshell_interface.cpp
+ xdgshell_v5_interface.cpp
)
ecm_add_wayland_server_protocol(SERVER_LIB_SRCS
@@ -113,6 +115,11 @@ ecm_add_wayland_server_protocol(SERVER_LIB_SRCS
BASENAME text-input-unstable-v2
)
+ecm_add_wayland_server_protocol(SERVER_LIB_SRCS
+ PROTOCOL ${KWAYLAND_SOURCE_DIR}/src/client/protocols/xdg-shell-unstable-v5.xml
+ BASENAME xdg-shell-v5
+)
+
add_library(KF5WaylandServer ${SERVER_LIB_SRCS})
generate_export_header(KF5WaylandServer
BASE_NAME
@@ -183,6 +190,7 @@ install(FILES
surface_interface.h
textinput_interface.h
touch_interface.h
+ xdgshell_interface.h
DESTINATION ${KF5_INCLUDE_INSTALL_DIR}/KWayland/Server COMPONENT Devel
)
diff --git a/src/wayland/autotests/client/CMakeLists.txt b/src/wayland/autotests/client/CMakeLists.txt
index cb2d29d827..b04a6087e8 100644
--- a/src/wayland/autotests/client/CMakeLists.txt
+++ b/src/wayland/autotests/client/CMakeLists.txt
@@ -330,3 +330,15 @@ add_executable(testSelection ${testSelection_SRCS})
target_link_libraries( testSelection Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer Wayland::Client)
add_test(kwayland-testSelection testSelection)
ecm_mark_as_test(testSelection)
+
+########################################################
+# Test XdgShellV5
+########################################################
+set( testXdgShellV5_SRCS
+ test_xdg_shell.cpp
+ )
+add_executable(testXdgShellV5 ${testXdgShellV5_SRCS})
+target_link_libraries( testXdgShellV5 Qt5::Test Qt5::Gui KF5::WaylandServer KF5::WaylandClient Wayland::Client)
+add_test(kwayland-testXdgShellV5 testXdgShellV5)
+ecm_mark_as_test(testXdgShellV5)
+
diff --git a/src/wayland/autotests/client/test_wayland_registry.cpp b/src/wayland/autotests/client/test_wayland_registry.cpp
index 13c335041a..32acff300e 100644
--- a/src/wayland/autotests/client/test_wayland_registry.cpp
+++ b/src/wayland/autotests/client/test_wayland_registry.cpp
@@ -30,6 +30,7 @@ License along with this library. If not, see .
#include "../../src/client/server_decoration.h"
#include "../../src/client/shell.h"
#include "../../src/client/subcompositor.h"
+#include "../../src/client/xdgshell.h"
#include "../../src/server/compositor_interface.h"
#include "../../src/server/datadevicemanager_interface.h"
#include "../../src/server/display.h"
@@ -45,12 +46,14 @@ License along with this library. If not, see .
#include "../../src/server/outputmanagement_interface.h"
#include "../../src/server/outputdevice_interface.h"
#include "../../src/server/textinput_interface.h"
+#include "../../src/server/xdgshell_interface.h"
// Wayland
#include
#include
#include
#include
#include
+#include
class TestWaylandRegistry : public QObject
{
@@ -76,6 +79,7 @@ private Q_SLOTS:
void testBindServerSideDecorationManager();
void testBindTextInputManagerUnstableV0();
void testBindTextInputManagerUnstableV2();
+ void testBindXdgShellUnstableV5();
void testGlobalSync();
void testGlobalSyncThreaded();
void testRemoval();
@@ -96,6 +100,7 @@ private:
KWayland::Server::ServerSideDecorationManagerInterface *m_serverSideDecorationManager;
KWayland::Server::TextInputManagerInterface *m_textInputManagerV0;
KWayland::Server::TextInputManagerInterface *m_textInputManagerV2;
+ KWayland::Server::XdgShellInterface *m_xdgShellUnstableV5;
};
static const QString s_socketName = QStringLiteral("kwin-test-wayland-registry-0");
@@ -114,6 +119,7 @@ TestWaylandRegistry::TestWaylandRegistry(QObject *parent)
, m_serverSideDecorationManager(nullptr)
, m_textInputManagerV0(nullptr)
, m_textInputManagerV2(nullptr)
+ , m_xdgShellUnstableV5(nullptr)
{
}
@@ -152,6 +158,9 @@ void TestWaylandRegistry::init()
m_textInputManagerV2 = m_display->createTextInputManager(KWayland::Server::TextInputInterfaceVersion::UnstableV2);
QCOMPARE(m_textInputManagerV2->interfaceVersion(), KWayland::Server::TextInputInterfaceVersion::UnstableV2);
m_textInputManagerV2->create();
+ m_xdgShellUnstableV5 = m_display->createXdgShell(KWayland::Server::XdgShellInterfaceVersion::UnstableV5);
+ m_xdgShellUnstableV5->create();
+ QCOMPARE(m_xdgShellUnstableV5->interfaceVersion(), KWayland::Server::XdgShellInterfaceVersion::UnstableV5);
}
void TestWaylandRegistry::cleanup()
@@ -289,6 +298,11 @@ void TestWaylandRegistry::testBindTextInputManagerUnstableV2()
TEST_BIND(KWayland::Client::Registry::Interface::TextInputManagerUnstableV2, SIGNAL(textInputManagerUnstableV2Announced(quint32,quint32)), bindTextInputManagerUnstableV2, zwp_text_input_manager_v2_destroy)
}
+void TestWaylandRegistry::testBindXdgShellUnstableV5()
+{
+ TEST_BIND(KWayland::Client::Registry::Interface::XdgShellUnstableV5, SIGNAL(xdgShellUnstableV5Announced(quint32,quint32)), bindXdgShellUnstableV5, xdg_shell_destroy)
+}
+
#undef TEST_BIND
void TestWaylandRegistry::testRemoval()
diff --git a/src/wayland/autotests/client/test_xdg_shell.cpp b/src/wayland/autotests/client/test_xdg_shell.cpp
new file mode 100644
index 0000000000..c62630f906
--- /dev/null
+++ b/src/wayland/autotests/client/test_xdg_shell.cpp
@@ -0,0 +1,649 @@
+/********************************************************************
+Copyright 2016 Martin Gräßlin
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) version 3, or any
+later version accepted by the membership of KDE e.V. (or its
+successor approved by the membership of KDE e.V.), which shall
+act as a proxy defined in Section 6 of version 3 of the license.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library. If not, see .
+*********************************************************************/
+// Qt
+#include
+// client
+#include "../../src/client/xdgshell.h"
+#include "../../src/client/connection_thread.h"
+#include "../../src/client/compositor.h"
+#include "../../src/client/event_queue.h"
+#include "../../src/client/registry.h"
+#include "../../src/client/output.h"
+#include "../../src/client/seat.h"
+#include "../../src/client/shm_pool.h"
+#include "../../src/client/surface.h"
+// server
+#include "../../src/server/display.h"
+#include "../../src/server/compositor_interface.h"
+#include "../../src/server/output_interface.h"
+#include "../../src/server/seat_interface.h"
+#include "../../src/server/surface_interface.h"
+#include "../../src/server/xdgshell_interface.h"
+
+using namespace KWayland::Client;
+using namespace KWayland::Server;
+
+Q_DECLARE_METATYPE(Qt::MouseButton)
+
+class XdgShellTest : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void init();
+ void cleanup();
+
+ void testCreateSurface();
+ void testTitle();
+ void testWindowClass();
+ void testMaximize();
+ void testMinimize();
+ void testFullscreen();
+ void testShowWindowMenu();
+ void testMove();
+ void testResize_data();
+ void testResize();
+ void testTransient();
+ void testClose();
+ void testConfigureStates_data();
+ void testConfigureStates();
+ void testConfigureMultipleAcks();
+ void testPopup();
+
+private:
+ Display *m_display = nullptr;
+ CompositorInterface *m_compositorInterface = nullptr;
+ OutputInterface *m_o1Interface = nullptr;
+ OutputInterface *m_o2Interface = nullptr;
+ SeatInterface *m_seatInterface = nullptr;
+ XdgShellInterface *m_xdgShellInterface = nullptr;
+ ConnectionThread *m_connection = nullptr;
+ QThread *m_thread = nullptr;
+ EventQueue *m_queue = nullptr;
+ Compositor *m_compositor = nullptr;
+ ShmPool *m_shmPool = nullptr;
+ XdgShell *m_xdgShell = nullptr;
+ Output *m_output1 = nullptr;
+ Output *m_output2 = nullptr;
+ Seat *m_seat = nullptr;
+};
+
+static const QString s_socketName = QStringLiteral("kwayland-test-xdg_shell-0");
+
+void XdgShellTest::init()
+{
+ delete m_display;
+ m_display = new Display(this);
+ m_display->setSocketName(s_socketName);
+ m_display->start();
+ QVERIFY(m_display->isRunning());
+ m_display->createShm();
+ m_o1Interface = m_display->createOutput(m_display);
+ m_o1Interface->addMode(QSize(1024, 768));
+ m_o1Interface->create();
+ m_o2Interface = m_display->createOutput(m_display);
+ m_o2Interface->addMode(QSize(1024, 768));
+ m_o2Interface->create();
+ m_seatInterface = m_display->createSeat(m_display);
+ m_seatInterface->setHasKeyboard(true);
+ m_seatInterface->setHasPointer(true);
+ m_seatInterface->setHasTouch(true);
+ m_seatInterface->create();
+ m_compositorInterface = m_display->createCompositor(m_display);
+ m_compositorInterface->create();
+ m_xdgShellInterface = m_display->createXdgShell(XdgShellInterfaceVersion::UnstableV5, m_display);
+ QCOMPARE(m_xdgShellInterface->interfaceVersion(), XdgShellInterfaceVersion::UnstableV5);
+ m_xdgShellInterface->create();
+
+ // setup connection
+ m_connection = new KWayland::Client::ConnectionThread;
+ QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected);
+ QVERIFY(connectedSpy.isValid());
+ m_connection->setSocketName(s_socketName);
+
+ m_thread = new QThread(this);
+ m_connection->moveToThread(m_thread);
+ m_thread->start();
+
+ m_connection->initConnection();
+ QVERIFY(connectedSpy.wait());
+
+ m_queue = new EventQueue(this);
+ m_queue->setup(m_connection);
+
+ Registry registry;
+ QSignalSpy interfacesAnnouncedSpy(®istry, &Registry::interfacesAnnounced);
+ QVERIFY(interfacesAnnouncedSpy.isValid());
+ QSignalSpy interfaceAnnouncedSpy(®istry, &Registry::interfaceAnnounced);
+ QVERIFY(interfaceAnnouncedSpy.isValid());
+ QSignalSpy outputAnnouncedSpy(®istry, &Registry::outputAnnounced);
+ QVERIFY(outputAnnouncedSpy.isValid());
+ QSignalSpy xdgShellAnnouncedSpy(®istry, &Registry::xdgShellUnstableV5Announced);
+ QVERIFY(xdgShellAnnouncedSpy.isValid());
+ registry.setEventQueue(m_queue);
+ registry.create(m_connection);
+ QVERIFY(registry.isValid());
+ registry.setup();
+ QVERIFY(interfacesAnnouncedSpy.wait());
+
+ QCOMPARE(outputAnnouncedSpy.count(), 2);
+ m_output1 = registry.createOutput(outputAnnouncedSpy.first().at(0).value(), outputAnnouncedSpy.first().at(1).value(), this);
+ m_output2 = registry.createOutput(outputAnnouncedSpy.last().at(0).value(), outputAnnouncedSpy.last().at(1).value(), this);
+
+ m_shmPool = registry.createShmPool(registry.interface(Registry::Interface::Shm).name, registry.interface(Registry::Interface::Shm).version, this);
+ QVERIFY(m_shmPool);
+ QVERIFY(m_shmPool->isValid());
+
+ m_compositor = registry.createCompositor(registry.interface(Registry::Interface::Compositor).name, registry.interface(Registry::Interface::Compositor).version, this);
+ QVERIFY(m_compositor);
+ QVERIFY(m_compositor->isValid());
+
+ m_seat = registry.createSeat(registry.interface(Registry::Interface::Seat).name, registry.interface(Registry::Interface::Seat).version, this);
+ QVERIFY(m_seat);
+ QVERIFY(m_seat->isValid());
+
+ QCOMPARE(xdgShellAnnouncedSpy.count(), 1);
+
+ m_xdgShell = registry.createXdgShell(registry.interface(Registry::Interface::XdgShellUnstableV5).name,
+ registry.interface(Registry::Interface::XdgShellUnstableV5).version,
+ this);
+ QVERIFY(m_xdgShell);
+ QVERIFY(m_xdgShell->isValid());
+}
+
+void XdgShellTest::cleanup()
+{
+#define CLEANUP(variable) \
+ if (variable) { \
+ delete variable; \
+ variable = nullptr; \
+ }
+ CLEANUP(m_xdgShell)
+ CLEANUP(m_compositor)
+ CLEANUP(m_shmPool)
+ CLEANUP(m_output1)
+ CLEANUP(m_output2)
+ CLEANUP(m_seat)
+ CLEANUP(m_queue)
+ if (m_connection) {
+ m_connection->deleteLater();
+ m_connection = nullptr;
+ }
+ if (m_thread) {
+ m_thread->quit();
+ m_thread->wait();
+ delete m_thread;
+ m_thread = nullptr;
+ }
+
+ CLEANUP(m_compositorInterface)
+ CLEANUP(m_xdgShellInterface)
+ CLEANUP(m_o1Interface);
+ CLEANUP(m_o2Interface);
+ CLEANUP(m_seatInterface);
+ CLEANUP(m_display)
+#undef CLEANUP
+}
+
+void XdgShellTest::testCreateSurface()
+{
+ // this test verifies that we can create a surface
+ // first created the signal spies for the server
+ QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
+ QVERIFY(surfaceCreatedSpy.isValid());
+ QSignalSpy xdgSurfaceCreatedSpy(m_xdgShellInterface, &XdgShellInterface::surfaceCreated);
+ QVERIFY(xdgSurfaceCreatedSpy.isValid());
+
+ // create surface
+ QScopedPointer surface(m_compositor->createSurface());
+ QVERIFY(!surface.isNull());
+ QVERIFY(surfaceCreatedSpy.wait());
+ auto serverSurface = surfaceCreatedSpy.first().first().value();
+ QVERIFY(serverSurface);
+
+ // create shell surface
+ QScopedPointer xdgSurface(m_xdgShell->createSurface(surface.data()));
+ QVERIFY(!xdgSurface.isNull());
+ QVERIFY(xdgSurfaceCreatedSpy.wait());
+ // verify base things
+ auto serverXdgSurface = xdgSurfaceCreatedSpy.first().first().value();
+ QVERIFY(serverXdgSurface);
+ QCOMPARE(serverXdgSurface->isConfigurePending(), false);
+ QCOMPARE(serverXdgSurface->title(), QString());
+ QCOMPARE(serverXdgSurface->windowClass(), QByteArray());
+ QCOMPARE(serverXdgSurface->isTransient(), false);
+ QCOMPARE(serverXdgSurface->transientFor(), QPointer());
+ QCOMPARE(serverXdgSurface->surface(), serverSurface);
+
+ // now let's destroy it
+ QSignalSpy destroyedSpy(serverXdgSurface, &QObject::destroyed);
+ QVERIFY(destroyedSpy.isValid());
+ xdgSurface.reset();
+ QVERIFY(destroyedSpy.wait());
+}
+
+#define SURFACE \
+ QSignalSpy xdgSurfaceCreatedSpy(m_xdgShellInterface, &XdgShellInterface::surfaceCreated); \
+ QVERIFY(xdgSurfaceCreatedSpy.isValid()); \
+ QScopedPointer surface(m_compositor->createSurface()); \
+ QScopedPointer xdgSurface(m_xdgShell->createSurface(surface.data())); \
+ QCOMPARE(xdgSurface->size(), QSize()); \
+ QVERIFY(xdgSurfaceCreatedSpy.wait()); \
+ auto serverXdgSurface = xdgSurfaceCreatedSpy.first().first().value(); \
+ QVERIFY(serverXdgSurface);
+
+void XdgShellTest::testTitle()
+{
+ // this test verifies that we can change the title of a shell surface
+ // first create surface
+ SURFACE
+
+ // should not have a title yet
+ QCOMPARE(serverXdgSurface->title(), QString());
+
+ // lets' change the title
+ QSignalSpy titleChangedSpy(serverXdgSurface, &XdgShellSurfaceInterface::titleChanged);
+ QVERIFY(titleChangedSpy.isValid());
+ xdgSurface->setTitle(QStringLiteral("foo"));
+ QVERIFY(titleChangedSpy.wait());
+ QCOMPARE(titleChangedSpy.count(), 1);
+ QCOMPARE(titleChangedSpy.first().first().toString(), QStringLiteral("foo"));
+ QCOMPARE(serverXdgSurface->title(), QStringLiteral("foo"));
+}
+
+void XdgShellTest::testWindowClass()
+{
+ // this test verifies that we can change the window class/app id of a shell surface
+ // first create surface
+ SURFACE
+
+ // should not have a window class yet
+ QCOMPARE(serverXdgSurface->windowClass(), QByteArray());
+
+ // let's change the window class
+ QSignalSpy windowClassChanged(serverXdgSurface, &XdgShellSurfaceInterface::windowClassChanged);
+ QVERIFY(windowClassChanged.isValid());
+ xdgSurface->setAppId(QByteArrayLiteral("org.kde.xdgsurfacetest"));
+ QVERIFY(windowClassChanged.wait());
+ QCOMPARE(windowClassChanged.count(), 1);
+ QCOMPARE(windowClassChanged.first().first().toByteArray(), QByteArrayLiteral("org.kde.xdgsurfacetest"));
+ QCOMPARE(serverXdgSurface->windowClass(), QByteArrayLiteral("org.kde.xdgsurfacetest"));
+}
+
+void XdgShellTest::testMaximize()
+{
+ // this test verifies that the maximize/unmaximize calls work
+ SURFACE
+
+ QSignalSpy maximizeRequestedSpy(serverXdgSurface, &XdgShellSurfaceInterface::maximizedChanged);
+ QVERIFY(maximizeRequestedSpy.isValid());
+
+ xdgSurface->setMaximized(true);
+ QVERIFY(maximizeRequestedSpy.wait());
+ QCOMPARE(maximizeRequestedSpy.count(), 1);
+ QCOMPARE(maximizeRequestedSpy.last().first().toBool(), true);
+
+ xdgSurface->setMaximized(false);
+ QVERIFY(maximizeRequestedSpy.wait());
+ QCOMPARE(maximizeRequestedSpy.count(), 2);
+ QCOMPARE(maximizeRequestedSpy.last().first().toBool(), false);
+}
+
+void XdgShellTest::testMinimize()
+{
+ // this test verifies that the minimize request is delivered
+ SURFACE
+
+ QSignalSpy minimizeRequestedSpy(serverXdgSurface, &XdgShellSurfaceInterface::minimizeRequested);
+ QVERIFY(minimizeRequestedSpy.isValid());
+
+ xdgSurface->requestMinimize();
+ QVERIFY(minimizeRequestedSpy.wait());
+ QCOMPARE(minimizeRequestedSpy.count(), 1);
+}
+
+void XdgShellTest::testFullscreen()
+{
+ qRegisterMetaType();
+ // this test verifies going to/from fullscreen
+ QSignalSpy xdgSurfaceCreatedSpy(m_xdgShellInterface, &XdgShellInterface::surfaceCreated);
+ QVERIFY(xdgSurfaceCreatedSpy.isValid());
+ QScopedPointer surface(m_compositor->createSurface());
+ QScopedPointer xdgSurface(m_xdgShell->createSurface(surface.data()));
+ QVERIFY(xdgSurfaceCreatedSpy.wait());
+ auto serverXdgSurface = xdgSurfaceCreatedSpy.first().first().value();
+ QVERIFY(serverXdgSurface);
+
+ QSignalSpy fullscreenSpy(serverXdgSurface, &XdgShellSurfaceInterface::fullscreenChanged);
+ QVERIFY(fullscreenSpy.isValid());
+
+ // without an output
+ xdgSurface->setFullscreen(true, nullptr);
+ QVERIFY(fullscreenSpy.wait());
+ QCOMPARE(fullscreenSpy.count(), 1);
+ QCOMPARE(fullscreenSpy.last().at(0).toBool(), true);
+ QVERIFY(!fullscreenSpy.last().at(1).value());
+
+ // unset
+ xdgSurface->setFullscreen(false);
+ QVERIFY(fullscreenSpy.wait());
+ QCOMPARE(fullscreenSpy.count(), 2);
+ QCOMPARE(fullscreenSpy.last().at(0).toBool(), false);
+ QVERIFY(!fullscreenSpy.last().at(1).value());
+
+ // with outputs
+ xdgSurface->setFullscreen(true, m_output1);
+ QVERIFY(fullscreenSpy.wait());
+ QCOMPARE(fullscreenSpy.count(), 3);
+ QCOMPARE(fullscreenSpy.last().at(0).toBool(), true);
+ QCOMPARE(fullscreenSpy.last().at(1).value(), m_o1Interface);
+
+ // now other output
+ xdgSurface->setFullscreen(true, m_output2);
+ QVERIFY(fullscreenSpy.wait());
+ QCOMPARE(fullscreenSpy.count(), 4);
+ QCOMPARE(fullscreenSpy.last().at(0).toBool(), true);
+ QCOMPARE(fullscreenSpy.last().at(1).value(), m_o2Interface);
+}
+
+void XdgShellTest::testShowWindowMenu()
+{
+ qRegisterMetaType();
+ // this test verifies that the show window menu request works
+ SURFACE
+
+ QSignalSpy windowMenuSpy(serverXdgSurface, &XdgShellSurfaceInterface::windowMenuRequested);
+ QVERIFY(windowMenuSpy.isValid());
+
+ // TODO: the serial needs to be a proper one
+ xdgSurface->requestShowWindowMenu(m_seat, 20, QPoint(30, 40));
+ QVERIFY(windowMenuSpy.wait());
+ QCOMPARE(windowMenuSpy.count(), 1);
+ QCOMPARE(windowMenuSpy.first().at(0).value(), m_seatInterface);
+ QCOMPARE(windowMenuSpy.first().at(1).value(), 20u);
+ QCOMPARE(windowMenuSpy.first().at(2).toPoint(), QPoint(30, 40));
+}
+
+void XdgShellTest::testMove()
+{
+ qRegisterMetaType();
+ // this test verifies that the move request works
+ SURFACE
+
+ QSignalSpy moveSpy(serverXdgSurface, &XdgShellSurfaceInterface::moveRequested);
+ QVERIFY(moveSpy.isValid());
+
+ // TODO: the serial needs to be a proper one
+ xdgSurface->requestMove(m_seat, 50);
+ QVERIFY(moveSpy.wait());
+ QCOMPARE(moveSpy.count(), 1);
+ QCOMPARE(moveSpy.first().at(0).value(), m_seatInterface);
+ QCOMPARE(moveSpy.first().at(1).value(), 50u);
+}
+
+void XdgShellTest::testResize_data()
+{
+ QTest::addColumn("edges");
+
+ QTest::newRow("none") << Qt::Edges();
+ QTest::newRow("top") << Qt::Edges(Qt::TopEdge);
+ QTest::newRow("bottom") << Qt::Edges(Qt::BottomEdge);
+ QTest::newRow("left") << Qt::Edges(Qt::LeftEdge);
+ QTest::newRow("top left") << Qt::Edges(Qt::TopEdge | Qt::LeftEdge);
+ QTest::newRow("bottom left") << Qt::Edges(Qt::BottomEdge | Qt::LeftEdge);
+ QTest::newRow("right") << Qt::Edges(Qt::RightEdge);
+ QTest::newRow("top right") << Qt::Edges(Qt::TopEdge | Qt::RightEdge);
+ QTest::newRow("bottom right") << Qt::Edges(Qt::BottomEdge | Qt::RightEdge);
+}
+
+void XdgShellTest::testResize()
+{
+ qRegisterMetaType();
+ // this test verifies that the resize request works
+ SURFACE
+
+ QSignalSpy resizeSpy(serverXdgSurface, &XdgShellSurfaceInterface::resizeRequested);
+ QVERIFY(resizeSpy.isValid());
+
+ // TODO: the serial needs to be a proper one
+ QFETCH(Qt::Edges, edges);
+ xdgSurface->requestResize(m_seat, 60, edges);
+ QVERIFY(resizeSpy.wait());
+ QCOMPARE(resizeSpy.count(), 1);
+ QCOMPARE(resizeSpy.first().at(0).value(), m_seatInterface);
+ QCOMPARE(resizeSpy.first().at(1).value(), 60u);
+ QCOMPARE(resizeSpy.first().at(2).value(), edges);
+}
+
+void XdgShellTest::testTransient()
+{
+ // this test verifies that setting the transient for works
+ SURFACE
+ QScopedPointer surface2(m_compositor->createSurface());
+ QScopedPointer xdgSurface2(m_xdgShell->createSurface(surface2.data()));
+ QVERIFY(xdgSurfaceCreatedSpy.wait());
+ auto serverXdgSurface2 = xdgSurfaceCreatedSpy.last().first().value();
+ QVERIFY(serverXdgSurface2);
+
+ QVERIFY(!serverXdgSurface->isTransient());
+ QVERIFY(!serverXdgSurface2->isTransient());
+
+ // now make xdsgSurface2 a transient for xdgSurface
+ QSignalSpy transientForSpy(serverXdgSurface2, &XdgShellSurfaceInterface::transientForChanged);
+ QVERIFY(transientForSpy.isValid());
+ xdgSurface2->setTransientFor(xdgSurface.data());
+
+ QVERIFY(transientForSpy.wait());
+ QCOMPARE(transientForSpy.count(), 1);
+ QVERIFY(serverXdgSurface2->isTransient());
+ QCOMPARE(serverXdgSurface2->transientFor().data(), serverXdgSurface);
+ QVERIFY(!serverXdgSurface->isTransient());
+
+ // unset the transient for
+ xdgSurface2->setTransientFor(nullptr);
+ QVERIFY(transientForSpy.wait());
+ QCOMPARE(transientForSpy.count(), 2);
+ QVERIFY(!serverXdgSurface2->isTransient());
+ QVERIFY(serverXdgSurface2->transientFor().isNull());
+ QVERIFY(!serverXdgSurface->isTransient());
+}
+
+void XdgShellTest::testClose()
+{
+ // this test verifies that a close request is sent to the client
+ SURFACE
+
+ QSignalSpy closeSpy(xdgSurface.data(), &XdgShellSurface::closeRequested);
+ QVERIFY(closeSpy.isValid());
+
+ serverXdgSurface->close();
+ QVERIFY(closeSpy.wait());
+ QCOMPARE(closeSpy.count(), 1);
+
+ QSignalSpy destroyedSpy(serverXdgSurface, &XdgShellSurfaceInterface::destroyed);
+ QVERIFY(destroyedSpy.isValid());
+ xdgSurface.reset();
+ QVERIFY(destroyedSpy.wait());
+}
+
+void XdgShellTest::testConfigureStates_data()
+{
+ QTest::addColumn("serverStates");
+ QTest::addColumn("clientStates");
+
+ const auto sa = XdgShellSurfaceInterface::States(XdgShellSurfaceInterface::State::Activated);
+ const auto sm = XdgShellSurfaceInterface::States(XdgShellSurfaceInterface::State::Maximized);
+ const auto sf = XdgShellSurfaceInterface::States(XdgShellSurfaceInterface::State::Fullscreen);
+ const auto sr = XdgShellSurfaceInterface::States(XdgShellSurfaceInterface::State::Resizing);
+
+ const auto ca = XdgShellSurface::States(XdgShellSurface::State::Activated);
+ const auto cm = XdgShellSurface::States(XdgShellSurface::State::Maximized);
+ const auto cf = XdgShellSurface::States(XdgShellSurface::State::Fullscreen);
+ const auto cr = XdgShellSurface::States(XdgShellSurface::State::Resizing);
+
+ QTest::newRow("none") << XdgShellSurfaceInterface::States() << XdgShellSurface::States();
+ QTest::newRow("Active") << sa << ca;
+ QTest::newRow("Maximize") << sm << cm;
+ QTest::newRow("Fullscreen") << sf << cf;
+ QTest::newRow("Resizing") << sr << cr;
+
+ QTest::newRow("Active/Maximize") << (sa | sm) << (ca | cm);
+ QTest::newRow("Active/Fullscreen") << (sa | sf) << (ca | cf);
+ QTest::newRow("Active/Resizing") << (sa | sr) << (ca | cr);
+ QTest::newRow("Maximize/Fullscreen") << (sm | sf) << (cm | cf);
+ QTest::newRow("Maximize/Resizing") << (sm | sr) << (cm | cr);
+ QTest::newRow("Fullscreen/Resizing") << (sf | sr) << (cf | cr);
+
+ QTest::newRow("Active/Maximize/Fullscreen") << (sa | sm | sf) << (ca | cm | cf);
+ QTest::newRow("Active/Maximize/Resizing") << (sa | sm | sr) << (ca | cm | cr);
+ QTest::newRow("Maximize/Fullscreen|Resizing") << (sm | sf | sr) << (cm | cf | cr);
+
+ QTest::newRow("Active/Maximize/Fullscreen/Resizing") << (sa | sm | sf | sr) << (ca | cm | cf | cr);
+}
+
+void XdgShellTest::testConfigureStates()
+{
+ qRegisterMetaType();
+ // this test verifies that configure states works
+ SURFACE
+
+ QSignalSpy configureSpy(xdgSurface.data(), &XdgShellSurface::configureRequested);
+ QVERIFY(configureSpy.isValid());
+
+ QFETCH(XdgShellSurfaceInterface::States, serverStates);
+ serverXdgSurface->configure(serverStates);
+ QVERIFY(configureSpy.wait());
+ QCOMPARE(configureSpy.count(), 1);
+ QCOMPARE(configureSpy.first().at(0).toSize(), QSize(0, 0));
+ QTEST(configureSpy.first().at(1).value(), "clientStates");
+ QCOMPARE(configureSpy.first().at(2).value(), m_display->serial());
+
+ QSignalSpy ackSpy(serverXdgSurface, &XdgShellSurfaceInterface::configureAcknowledged);
+ QVERIFY(ackSpy.isValid());
+
+ xdgSurface->ackConfigure(configureSpy.first().at(2).value());
+ QVERIFY(ackSpy.wait());
+ QCOMPARE(ackSpy.count(), 1);
+ QCOMPARE(ackSpy.first().first().value(), configureSpy.first().at(2).value());
+}
+
+void XdgShellTest::testConfigureMultipleAcks()
+{
+ qRegisterMetaType();
+ // this test verifies that with multiple configure requests the last acknowledged one acknowledges all
+ SURFACE
+
+ QSignalSpy configureSpy(xdgSurface.data(), &XdgShellSurface::configureRequested);
+ QVERIFY(configureSpy.isValid());
+ QSignalSpy sizeChangedSpy(xdgSurface.data(), &XdgShellSurface::sizeChanged);
+ QVERIFY(sizeChangedSpy.isValid());
+ QSignalSpy ackSpy(serverXdgSurface, &XdgShellSurfaceInterface::configureAcknowledged);
+ QVERIFY(ackSpy.isValid());
+
+ serverXdgSurface->configure(XdgShellSurfaceInterface::States(), QSize(10, 20));
+ const quint32 serial1 = m_display->serial();
+ serverXdgSurface->configure(XdgShellSurfaceInterface::States(), QSize(20, 30));
+ const quint32 serial2 = m_display->serial();
+ QVERIFY(serial1 != serial2);
+ serverXdgSurface->configure(XdgShellSurfaceInterface::States(), QSize(30, 40));
+ const quint32 serial3 = m_display->serial();
+ QVERIFY(serial1 != serial3);
+ QVERIFY(serial2 != serial3);
+
+ QVERIFY(configureSpy.wait());
+ QCOMPARE(configureSpy.count(), 3);
+ QCOMPARE(configureSpy.at(0).at(0).toSize(), QSize(10, 20));
+ QCOMPARE(configureSpy.at(0).at(1).value(), XdgShellSurface::States());
+ QCOMPARE(configureSpy.at(0).at(2).value(), serial1);
+ QCOMPARE(configureSpy.at(1).at(0).toSize(), QSize(20, 30));
+ QCOMPARE(configureSpy.at(1).at(1).value(), XdgShellSurface::States());
+ QCOMPARE(configureSpy.at(1).at(2).value(), serial2);
+ QCOMPARE(configureSpy.at(2).at(0).toSize(), QSize(30, 40));
+ QCOMPARE(configureSpy.at(2).at(1).value(), XdgShellSurface::States());
+ QCOMPARE(configureSpy.at(2).at(2).value(), serial3);
+ QCOMPARE(sizeChangedSpy.count(), 3);
+ QCOMPARE(sizeChangedSpy.at(0).at(0).toSize(), QSize(10, 20));
+ QCOMPARE(sizeChangedSpy.at(1).at(0).toSize(), QSize(20, 30));
+ QCOMPARE(sizeChangedSpy.at(2).at(0).toSize(), QSize(30, 40));
+ QCOMPARE(xdgSurface->size(), QSize(30, 40));
+
+ xdgSurface->ackConfigure(serial3);
+ QVERIFY(ackSpy.wait());
+ QCOMPARE(ackSpy.count(), 3);
+ QCOMPARE(ackSpy.at(0).first().value(), serial1);
+ QCOMPARE(ackSpy.at(1).first().value(), serial2);
+ QCOMPARE(ackSpy.at(2).first().value(), serial3);
+
+ // configure once more with a null size
+ serverXdgSurface->configure(XdgShellSurfaceInterface::States());
+ // should not change size
+ QVERIFY(configureSpy.wait());
+ QCOMPARE(configureSpy.count(), 4);
+ QCOMPARE(sizeChangedSpy.count(), 3);
+ QCOMPARE(xdgSurface->size(), QSize(30, 40));
+}
+
+void XdgShellTest::testPopup()
+{
+ // this test verifies that the creation of popups works correctly
+ SURFACE
+ QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
+ QVERIFY(surfaceCreatedSpy.isValid());
+ QSignalSpy xdgPopupSpy(m_xdgShellInterface, &XdgShellInterface::popupCreated);
+ QVERIFY(xdgPopupSpy.isValid());
+
+ QScopedPointer popupSurface(m_compositor->createSurface());
+ QVERIFY(surfaceCreatedSpy.wait());
+
+ // TODO: proper serial
+ QScopedPointer xdgPopup(m_xdgShell->createPopup(popupSurface.data(), surface.data(), m_seat, 120, QPoint(10, 20)));
+ QVERIFY(xdgPopupSpy.wait());
+ QCOMPARE(xdgPopupSpy.count(), 1);
+ QCOMPARE(xdgPopupSpy.first().at(1).value(), m_seatInterface);
+ QCOMPARE(xdgPopupSpy.first().at(2).value(), 120u);
+ auto serverXdgPopup = xdgPopupSpy.first().first().value();
+ QVERIFY(serverXdgPopup);
+
+ QCOMPARE(serverXdgPopup->surface(), surfaceCreatedSpy.first().first().value());
+ QCOMPARE(serverXdgPopup->transientFor().data(), serverXdgSurface->surface());
+ QCOMPARE(serverXdgPopup->transientOffset(), QPoint(10, 20));
+
+ // now also a popup for the popup
+ QScopedPointer popup2Surface(m_compositor->createSurface());
+ QScopedPointer xdgPopup2(m_xdgShell->createPopup(popup2Surface.data(), popupSurface.data(), m_seat, 121, QPoint(5, 7)));
+ QVERIFY(xdgPopupSpy.wait());
+ QCOMPARE(xdgPopupSpy.count(), 2);
+ QCOMPARE(xdgPopupSpy.last().at(1).value(), m_seatInterface);
+ QCOMPARE(xdgPopupSpy.last().at(2).value(), 121u);
+ auto serverXdgPopup2 = xdgPopupSpy.last().first().value();
+ QVERIFY(serverXdgPopup2);
+
+ QCOMPARE(serverXdgPopup2->surface(), surfaceCreatedSpy.last().first().value());
+ QCOMPARE(serverXdgPopup2->transientFor().data(), serverXdgPopup->surface());
+ QCOMPARE(serverXdgPopup2->transientOffset(), QPoint(5, 7));
+
+ QSignalSpy popup2DoneSpy(xdgPopup2.data(), &XdgShellPopup::popupDone);
+ QVERIFY(popup2DoneSpy.isValid());
+ serverXdgPopup2->popupDone();
+ QVERIFY(popup2DoneSpy.wait());
+ // TODO: test that this sends also the done to all parents
+}
+
+QTEST_GUILESS_MAIN(XdgShellTest)
+#include "test_xdg_shell.moc"
diff --git a/src/wayland/display.cpp b/src/wayland/display.cpp
index 2c77456546..3a6dc17e7f 100644
--- a/src/wayland/display.cpp
+++ b/src/wayland/display.cpp
@@ -40,6 +40,7 @@ License along with this library. If not, see .
#include "shell_interface.h"
#include "subcompositor_interface.h"
#include "textinput_interface_p.h"
+#include "xdgshell_v5_interface_p.h"
#include
#include
@@ -356,6 +357,18 @@ TextInputManagerInterface *Display::createTextInputManager(const TextInputInterf
return t;
}
+XdgShellInterface *Display::createXdgShell(const XdgShellInterfaceVersion &version, QObject *parent)
+{
+ XdgShellInterface *x = nullptr;
+ switch (version) {
+ case XdgShellInterfaceVersion::UnstableV5:
+ x = new XdgShellV5Interface(this, parent);
+ break;
+ }
+ connect(this, &Display::aboutToTerminate, x, [x] { delete x; });
+ return x;
+}
+
void Display::createShm()
{
Q_ASSERT(d->display);
diff --git a/src/wayland/display.h b/src/wayland/display.h
index c4b6741638..e56aa19794 100644
--- a/src/wayland/display.h
+++ b/src/wayland/display.h
@@ -71,6 +71,9 @@ class ShellInterface;
class SubCompositorInterface;
enum class TextInputInterfaceVersion;
class TextInputManagerInterface;
+class XdgShellV5Interface;
+enum class XdgShellInterfaceVersion;
+class XdgShellInterface;
/**
* @brief Class holding the Wayland server display loop.
@@ -179,6 +182,13 @@ public:
**/
TextInputManagerInterface *createTextInputManager(const TextInputInterfaceVersion &version, QObject *parent = nullptr);
+ /**
+ * Creates the XdgShell in interface @p version.
+ *
+ * @since 5.25
+ **/
+ XdgShellInterface *createXdgShell(const XdgShellInterfaceVersion &version, QObject *parent = nullptr);
+
/**
* Gets the ClientConnection for the given @p client.
* If there is no ClientConnection yet for the given @p client, it will be created.
diff --git a/src/wayland/server/generic_shell_surface_p.h b/src/wayland/server/generic_shell_surface_p.h
new file mode 100644
index 0000000000..3c39d325c2
--- /dev/null
+++ b/src/wayland/server/generic_shell_surface_p.h
@@ -0,0 +1,129 @@
+/********************************************************************
+Copyright 2016 Martin Gräßlin
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) version 3, or any
+later version accepted by the membership of KDE e.V. (or its
+successor approved by the membership of KDE e.V.), which shall
+act as a proxy defined in Section 6 of version 3 of the license.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library. If not, see .
+*********************************************************************/
+#ifndef KWAYLAND_SERVER_GENERIC_SHELL_SURFACE_P_H
+#define KWAYLAND_SERVER_GENERIC_SHELL_SURFACE_P_H
+
+#include "seat_interface.h"
+#include "surface_interface.h"
+#include
+
+namespace KWayland
+{
+
+namespace Server
+{
+
+template
+class GenericShellSurface
+{
+public:
+ GenericShellSurface(T *shellSurface, SurfaceInterface *surface)
+ : surface(surface)
+ , shellSurface(shellSurface)
+ {}
+
+ SurfaceInterface *surface;
+ QString title;
+ QByteArray windowClass;
+
+protected:
+ void setTitle(const QString &title);
+ void setWindowClass(const QByteArray &wc);
+
+ static void moveCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial);
+ template
+ static void resizeCallback(wl_client *client, wl_resource *resource, wl_resource * seat, uint32_t serial, uint32_t edges);
+ static void setTitleCallback(wl_client *client, wl_resource *resource, const char *title);
+ static void setAppIdCallback(wl_client *client, wl_resource *resource, const char *app_id);
+
+private:
+ T *q_func() {
+ return shellSurface;
+ }
+ static typename T::Private *userData(wl_resource *resource) {
+ return reinterpret_cast(wl_resource_get_user_data(resource));
+ }
+ T *shellSurface;
+};
+
+template
+void GenericShellSurface::setTitleCallback(wl_client *client, wl_resource *resource, const char *title)
+{
+ auto s = userData(resource);
+ Q_ASSERT(client == *s->client);
+ s->setTitle(QString::fromUtf8(title));
+}
+
+template
+void GenericShellSurface::setAppIdCallback(wl_client *client, wl_resource *resource, const char *app_id)
+{
+ auto s = userData(resource);
+ Q_ASSERT(client == *s->client);
+ s->setWindowClass(QByteArray(app_id));
+}
+
+template
+void GenericShellSurface::setTitle(const QString &t)
+{
+ if (title == t) {
+ return;
+ }
+ title = t;
+ Q_Q(T);
+ emit q->titleChanged(title);
+}
+
+template
+void GenericShellSurface::setWindowClass(const QByteArray &wc)
+{
+ if (windowClass == wc) {
+ return;
+ }
+ windowClass = wc;
+ Q_Q(T);
+ emit q->windowClassChanged(windowClass);
+}
+
+template
+void GenericShellSurface::moveCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial)
+{
+ auto s = userData(resource);
+ Q_ASSERT(client == *s->client);
+ emit s->q_func()->moveRequested(SeatInterface::get(seat), serial);
+}
+
+namespace {
+template
+Qt::Edges edgesToQtEdges(T edges);
+}
+
+template
+template
+void GenericShellSurface::resizeCallback(wl_client *client, wl_resource *resource, wl_resource * seat, uint32_t serial, uint32_t edges)
+{
+ auto s = userData(resource);
+ Q_ASSERT(client == *s->client);
+ emit s->q_func()->resizeRequested(SeatInterface::get(seat), serial, edgesToQtEdges(U(edges)));
+}
+
+}
+}
+
+#endif
diff --git a/src/wayland/server/shell_interface.cpp b/src/wayland/server/shell_interface.cpp
index 9a7226cca4..1fa414a6b4 100644
--- a/src/wayland/server/shell_interface.cpp
+++ b/src/wayland/server/shell_interface.cpp
@@ -18,6 +18,7 @@ You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see .
*********************************************************************/
#include "shell_interface.h"
+#include "generic_shell_surface_p.h"
#include "global_p.h"
#include "resource_p.h"
#include "display.h"
@@ -64,15 +65,12 @@ const struct wl_shell_interface ShellInterface::Private::s_interface = {
#endif
-class ShellSurfaceInterface::Private : public Resource::Private
+class ShellSurfaceInterface::Private : public Resource::Private, public GenericShellSurface
{
public:
Private(ShellSurfaceInterface *q, ShellInterface *shell, SurfaceInterface *surface, wl_resource *parentResource);
void ping();
- SurfaceInterface *surface;
- QString title;
- QByteArray windowClass;
QScopedPointer pingTimer;
quint32 pingSerial = 0;
enum class WindowMode {
@@ -87,12 +85,13 @@ public:
bool acceptsKeyboardFocus = true;
void setWindowMode(WindowMode newWindowMode);
+ ShellSurfaceInterface *q_func() {
+ return reinterpret_cast(q);
+ }
+
private:
// interface callbacks
static void pongCallback(wl_client *client, wl_resource *resource, uint32_t serial);
- static void moveCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial);
- static void resizeCallback(wl_client *client, wl_resource *resource, wl_resource *seat,
- uint32_t serial, uint32_t edges);
static void setToplevelCallback(wl_client *client, wl_resource *resource);
static void setTransientCallback(wl_client *client, wl_resource *resource, wl_resource *parent,
int32_t x, int32_t y, uint32_t flags);
@@ -101,16 +100,9 @@ private:
static void setPopupCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial,
wl_resource *parent, int32_t x, int32_t y, uint32_t flags);
static void setMaximizedCallback(wl_client *client, wl_resource *resource, wl_resource *output);
- static void setTitleCallback(wl_client *client, wl_resource *resource, const char *title);
- static void setClassCallback(wl_client *client, wl_resource *resource, const char *class_);
- void setTitle(const QString &title);
- void setWindowClass(const QByteArray &windowClass);
void pong(quint32 serial);
void setAcceptsFocus(quint32 flags);
- ShellSurfaceInterface *q_func() {
- return reinterpret_cast(q);
- }
static const struct wl_shell_surface_interface s_interface;
};
@@ -166,7 +158,7 @@ void ShellInterface::Private::createSurface(wl_client *client, uint32_t version,
*********************************/
ShellSurfaceInterface::Private::Private(ShellSurfaceInterface *q, ShellInterface *shell, SurfaceInterface *surface, wl_resource *parentResource)
: Resource::Private(q, shell, parentResource, &wl_shell_surface_interface, &s_interface)
- , surface(surface)
+ , GenericShellSurface(q, surface)
, pingTimer(new QTimer)
{
pingTimer->setSingleShot(true);
@@ -177,14 +169,14 @@ ShellSurfaceInterface::Private::Private(ShellSurfaceInterface *q, ShellInterface
const struct wl_shell_surface_interface ShellSurfaceInterface::Private::s_interface = {
pongCallback,
moveCallback,
- resizeCallback,
+ resizeCallback,
setToplevelCallback,
setTransientCallback,
setFullscreenCallback,
setPopupCallback,
setMaximizedCallback,
setTitleCallback,
- setClassCallback
+ setAppIdCallback
};
#endif
@@ -262,17 +254,10 @@ void ShellSurfaceInterface::requestSize(const QSize &size)
d->client->flush();
}
-void ShellSurfaceInterface::Private::moveCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial)
+namespace {
+template <>
+Qt::Edges edgesToQtEdges(wl_shell_surface_resize edges)
{
- auto s = cast(resource);
- Q_ASSERT(client == *s->client);
- emit s->q_func()->moveRequested(SeatInterface::get(seat), serial);
-}
-
-void ShellSurfaceInterface::Private::resizeCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial, uint32_t edges)
-{
- auto s = cast(resource);
- Q_ASSERT(client == *s->client);
Qt::Edges qtEdges;
switch (edges) {
case WL_SHELL_SURFACE_RESIZE_TOP:
@@ -299,10 +284,14 @@ void ShellSurfaceInterface::Private::resizeCallback(wl_client *client, wl_resour
case WL_SHELL_SURFACE_RESIZE_BOTTOM_RIGHT:
qtEdges = Qt::BottomEdge | Qt::RightEdge;
break;
+ case WL_SHELL_SURFACE_RESIZE_NONE:
+ break;
default:
+ Q_UNREACHABLE();
break;
}
- emit s->q_func()->resizeRequested(SeatInterface::get(seat), serial, qtEdges);
+ return qtEdges;
+}
}
void ShellSurfaceInterface::Private::setToplevelCallback(wl_client *client, wl_resource *resource)
@@ -401,40 +390,6 @@ void ShellSurfaceInterface::Private::setMaximizedCallback(wl_client *client, wl_
s->setWindowMode(WindowMode::Maximized);
}
-void ShellSurfaceInterface::Private::setTitleCallback(wl_client *client, wl_resource *resource, const char *title)
-{
- auto s = cast(resource);
- Q_ASSERT(client == *s->client);
- s->setTitle(QString::fromUtf8(title));
-}
-
-void ShellSurfaceInterface::Private::setTitle(const QString &t)
-{
- if (title == t) {
- return;
- }
- title = t;
- Q_Q(ShellSurfaceInterface);
- emit q->titleChanged(title);
-}
-
-void ShellSurfaceInterface::Private::setClassCallback(wl_client *client, wl_resource *resource, const char *class_)
-{
- auto s = cast(resource);
- Q_ASSERT(client == *s->client);
- s->setWindowClass(QByteArray(class_));
-}
-
-void ShellSurfaceInterface::Private::setWindowClass(const QByteArray &wc)
-{
- if (windowClass == wc) {
- return;
- }
- windowClass = wc;
- Q_Q(ShellSurfaceInterface);
- emit q->windowClassChanged(windowClass);
-}
-
SurfaceInterface *ShellSurfaceInterface::surface() const {
Q_D();
return d->surface;
diff --git a/src/wayland/server/shell_interface.h b/src/wayland/server/shell_interface.h
index d31e27fabf..95a0aca093 100644
--- a/src/wayland/server/shell_interface.h
+++ b/src/wayland/server/shell_interface.h
@@ -40,6 +40,8 @@ class Display;
class SeatInterface;
class SurfaceInterface;
class ShellSurfaceInterface;
+template
+class GenericShellSurface;
/**
* @brief Global for the wl_shell interface.
@@ -293,6 +295,7 @@ Q_SIGNALS:
private:
friend class ShellInterface;
explicit ShellSurfaceInterface(ShellInterface *shell, SurfaceInterface *parent, wl_resource *parentResource);
+ friend class GenericShellSurface;
class Private;
Private *d_func() const;
};
diff --git a/src/wayland/server/xdgshell_v5_interface.cpp b/src/wayland/server/xdgshell_v5_interface.cpp
new file mode 100644
index 0000000000..27dd59184d
--- /dev/null
+++ b/src/wayland/server/xdgshell_v5_interface.cpp
@@ -0,0 +1,475 @@
+/****************************************************************************
+Copyright 2016 Martin Gräßlin
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) version 3, or any
+later version accepted by the membership of KDE e.V. (or its
+successor approved by the membership of KDE e.V.), which shall
+act as a proxy defined in Section 6 of version 3 of the license.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library. If not, see .
+****************************************************************************/
+#include "xdgshell_v5_interface_p.h"
+#include "xdgshell_interface_p.h"
+#include "generic_shell_surface_p.h"
+#include "display.h"
+#include "global_p.h"
+#include "resource_p.h"
+#include "output_interface.h"
+#include "seat_interface.h"
+#include "surface_interface.h"
+
+#include
+
+namespace KWayland
+{
+namespace Server
+{
+
+class XdgShellV5Interface::Private : public XdgShellInterface::Private
+{
+public:
+ Private(XdgShellV5Interface *q, Display *d);
+
+ QVector surfaces;
+
+private:
+ void createSurface(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, wl_resource *parentResource);
+ void createPopup(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, SurfaceInterface *parent, SeatInterface *seat, quint32 serial, const QPoint &pos, wl_resource *parentResource);
+ void bind(wl_client *client, uint32_t version, uint32_t id) override;
+
+ static void unbind(wl_resource *resource);
+ static Private *cast(wl_resource *r) {
+ return reinterpret_cast(wl_resource_get_user_data(r));
+ }
+
+ static void destroyCallback(wl_client *client, wl_resource *resource);
+ static void useUnstableVersionCallback(wl_client *client, wl_resource *resource, int32_t version);
+ static void getXdgSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface);
+ static void getXdgPopupCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface, wl_resource * parent, wl_resource * seat, uint32_t serial, int32_t x, int32_t y);
+ static void pongCallback(wl_client *client, wl_resource *resource, uint32_t serial);
+
+ XdgShellV5Interface *q;
+ static const struct xdg_shell_interface s_interface;
+ static const quint32 s_version;
+};
+
+class XdgPopupV5Interface::Private : public XdgShellPopupInterface::Private
+{
+public:
+ Private(XdgPopupV5Interface *q, XdgShellV5Interface *c, SurfaceInterface *surface, wl_resource *parentResource);
+ ~Private();
+
+ void popupDone() override;
+
+ XdgPopupV5Interface *q_func() {
+ return reinterpret_cast(q);
+ }
+
+private:
+
+ static const struct xdg_popup_interface s_interface;
+};
+
+const quint32 XdgShellV5Interface::Private::s_version = 1;
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+const struct xdg_shell_interface XdgShellV5Interface::Private::s_interface = {
+ destroyCallback,
+ useUnstableVersionCallback,
+ getXdgSurfaceCallback,
+ getXdgPopupCallback,
+ pongCallback
+};
+#endif
+
+void XdgShellV5Interface::Private::destroyCallback(wl_client *client, wl_resource *resource)
+{
+ Q_UNUSED(client)
+ // TODO: send protocol error if there are still surfaces mapped
+ wl_resource_destroy(resource);
+}
+
+void XdgShellV5Interface::Private::useUnstableVersionCallback(wl_client *client, wl_resource *resource, int32_t version)
+{
+ Q_UNUSED(client)
+ Q_UNUSED(resource)
+ Q_UNUSED(version)
+ // TODO: implement
+}
+
+void XdgShellV5Interface::Private::getXdgSurfaceCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface)
+{
+ auto s = cast(resource);
+ s->createSurface(client, wl_resource_get_version(resource), id, SurfaceInterface::get(surface), resource);
+}
+
+void XdgShellV5Interface::Private::createSurface(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, wl_resource *parentResource)
+{
+ auto it = std::find_if(surfaces.constBegin(), surfaces.constEnd(),
+ [surface](XdgSurfaceV5Interface *s) {
+ return surface == s->surface();
+ }
+ );
+ if (it != surfaces.constEnd()) {
+ wl_resource_post_error(surface->resource(), XDG_SHELL_ERROR_ROLE, "ShellSurface already created");
+ return;
+ }
+ XdgSurfaceV5Interface *shellSurface = new XdgSurfaceV5Interface(q, surface, parentResource);
+ surfaces << shellSurface;
+ QObject::connect(shellSurface, &XdgSurfaceV5Interface::destroyed, q,
+ [this, shellSurface] {
+ surfaces.removeAll(shellSurface);
+ }
+ );
+ shellSurface->d->create(display->getConnection(client), version, id);
+ emit q->surfaceCreated(shellSurface);
+}
+
+void XdgShellV5Interface::Private::getXdgPopupCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface, wl_resource * parent, wl_resource * seat, uint32_t serial, int32_t x, int32_t y)
+{
+ auto s = cast(resource);
+ s->createPopup(client, wl_resource_get_version(resource), id, SurfaceInterface::get(surface), SurfaceInterface::get(parent), SeatInterface::get(seat), serial, QPoint(x, y), resource);
+}
+
+void XdgShellV5Interface::Private::createPopup(wl_client *client, uint32_t version, uint32_t id, SurfaceInterface *surface, SurfaceInterface *parent, SeatInterface *seat, quint32 serial, const QPoint &pos, wl_resource *parentResource)
+{
+ XdgPopupV5Interface *popupSurface = new XdgPopupV5Interface(q, surface, parentResource);
+ auto d = popupSurface->d_func();
+ d->parent = QPointer(parent);
+ d->transientOffset = pos;
+ d->create(display->getConnection(client), version, id);
+ emit q->popupCreated(popupSurface, seat, serial);
+}
+
+void XdgShellV5Interface::Private::pongCallback(wl_client *client, wl_resource *resource, uint32_t serial)
+{
+ Q_UNUSED(client)
+ Q_UNUSED(resource)
+ Q_UNUSED(serial)
+ // TODO: implement
+}
+
+XdgShellV5Interface::Private::Private(XdgShellV5Interface *q, Display *d)
+ : XdgShellInterface::Private(XdgShellInterfaceVersion::UnstableV5, q, d, &xdg_shell_interface, s_version)
+ , q(q)
+{
+}
+
+void XdgShellV5Interface::Private::bind(wl_client *client, uint32_t version, uint32_t id)
+{
+ auto c = display->getConnection(client);
+ wl_resource *resource = c->createResource(&xdg_shell_interface, qMin(version, s_version), id);
+ if (!resource) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+ wl_resource_set_implementation(resource, &s_interface, this, unbind);
+ // TODO: should we track, yes we need to track to be able to ping!
+}
+
+void XdgShellV5Interface::Private::unbind(wl_resource *resource)
+{
+ Q_UNUSED(resource)
+ // TODO: implement?
+}
+
+XdgSurfaceV5Interface *XdgShellV5Interface::getSurface(wl_resource *resource)
+{
+ if (!resource) {
+ return nullptr;
+ }
+ Q_D();
+ auto it = std::find_if(d->surfaces.constBegin(), d->surfaces.constEnd(),
+ [resource] (XdgSurfaceV5Interface *surface) {
+ return surface->resource() == resource;
+ }
+ );
+ if (it != d->surfaces.constEnd()) {
+ return *it;
+ }
+ return nullptr;
+}
+
+XdgShellV5Interface::Private *XdgShellV5Interface::d_func() const
+{
+ return reinterpret_cast(d.data());
+}
+
+class XdgSurfaceV5Interface::Private : public XdgShellSurfaceInterface::Private
+{
+public:
+ Private(XdgSurfaceV5Interface *q, XdgShellV5Interface *c, SurfaceInterface *surface, wl_resource *parentResource);
+ ~Private();
+
+ void close() override;
+ quint32 configure(States states, const QSize &size) override;
+
+ XdgSurfaceV5Interface *q_func() {
+ return reinterpret_cast(q);
+ }
+
+private:
+ static void setParentCallback(wl_client *client, wl_resource *resource, wl_resource * parent);
+ static void showWindowMenuCallback(wl_client *client, wl_resource *resource, wl_resource * seat, uint32_t serial, int32_t x, int32_t y);
+ static void ackConfigureCallback(wl_client *client, wl_resource *resource, uint32_t serial);
+ static void setWindowGeometryCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height);
+ static void setMaximizedCallback(wl_client *client, wl_resource *resource);
+ static void unsetMaximizedCallback(wl_client *client, wl_resource *resource);
+ static void setFullscreenCallback(wl_client *client, wl_resource *resource, wl_resource * output);
+ static void unsetFullscreenCallback(wl_client *client, wl_resource *resource);
+ static void setMinimizedCallback(wl_client *client, wl_resource *resource);
+
+ static const struct xdg_surface_interface s_interface;
+};
+
+namespace {
+template <>
+Qt::Edges edgesToQtEdges(xdg_surface_resize_edge edges)
+{
+ Qt::Edges qtEdges;
+ switch (edges) {
+ case XDG_SURFACE_RESIZE_EDGE_TOP:
+ qtEdges = Qt::TopEdge;
+ break;
+ case XDG_SURFACE_RESIZE_EDGE_BOTTOM:
+ qtEdges = Qt::BottomEdge;
+ break;
+ case XDG_SURFACE_RESIZE_EDGE_LEFT:
+ qtEdges = Qt::LeftEdge;
+ break;
+ case XDG_SURFACE_RESIZE_EDGE_TOP_LEFT:
+ qtEdges = Qt::TopEdge | Qt::LeftEdge;
+ break;
+ case XDG_SURFACE_RESIZE_EDGE_BOTTOM_LEFT:
+ qtEdges = Qt::BottomEdge | Qt::LeftEdge;
+ break;
+ case XDG_SURFACE_RESIZE_EDGE_RIGHT:
+ qtEdges = Qt::RightEdge;
+ break;
+ case XDG_SURFACE_RESIZE_EDGE_TOP_RIGHT:
+ qtEdges = Qt::TopEdge | Qt::RightEdge;
+ break;
+ case XDG_SURFACE_RESIZE_EDGE_BOTTOM_RIGHT:
+ qtEdges = Qt::BottomEdge | Qt::RightEdge;
+ break;
+ case XDG_SURFACE_RESIZE_EDGE_NONE:
+ break;
+ default:
+ Q_UNREACHABLE();
+ break;
+ }
+ return qtEdges;
+}
+}
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+const struct xdg_surface_interface XdgSurfaceV5Interface::Private::s_interface = {
+ resourceDestroyedCallback,
+ setParentCallback,
+ setTitleCallback,
+ setAppIdCallback,
+ showWindowMenuCallback,
+ moveCallback,
+ resizeCallback,
+ ackConfigureCallback,
+ setWindowGeometryCallback,
+ setMaximizedCallback,
+ unsetMaximizedCallback,
+ setFullscreenCallback,
+ unsetFullscreenCallback,
+ setMinimizedCallback
+};
+#endif
+
+void XdgSurfaceV5Interface::Private::setParentCallback(wl_client *client, wl_resource *resource, wl_resource *parent)
+{
+ auto s = cast(resource);
+ Q_ASSERT(client == *s->client);
+ auto parentSurface = static_cast(s->q->global())->getSurface(parent);
+ if (s->parent.data() != parentSurface) {
+ s->parent = QPointer(parentSurface);
+ emit s->q_func()->transientForChanged();
+ }
+}
+
+void XdgSurfaceV5Interface::Private::showWindowMenuCallback(wl_client *client, wl_resource *resource, wl_resource *seat, uint32_t serial, int32_t x, int32_t y)
+{
+ auto s = cast(resource);
+ Q_ASSERT(client == *s->client);
+ emit s->q_func()->windowMenuRequested(SeatInterface::get(seat), serial, QPoint(x, y));
+}
+
+void XdgSurfaceV5Interface::Private::ackConfigureCallback(wl_client *client, wl_resource *resource, uint32_t serial)
+{
+ auto s = cast(resource);
+ Q_ASSERT(client == *s->client);
+ if (!s->configureSerials.contains(serial)) {
+ // TODO: send error?
+ return;
+ }
+ while (!s->configureSerials.isEmpty()) {
+ quint32 i = s->configureSerials.takeFirst();
+ emit s->q_func()->configureAcknowledged(i);
+ if (i == serial) {
+ break;
+ }
+ }
+}
+
+void XdgSurfaceV5Interface::Private::setWindowGeometryCallback(wl_client *client, wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height)
+{
+ // TODO: implement
+ Q_UNUSED(client)
+ Q_UNUSED(resource)
+ Q_UNUSED(x)
+ Q_UNUSED(y)
+ Q_UNUSED(width)
+ Q_UNUSED(height)
+}
+
+void XdgSurfaceV5Interface::Private::setMaximizedCallback(wl_client *client, wl_resource *resource)
+{
+ auto s = cast(resource);
+ Q_ASSERT(client == *s->client);
+ s->q_func()->maximizedChanged(true);
+}
+
+void XdgSurfaceV5Interface::Private::unsetMaximizedCallback(wl_client *client, wl_resource *resource)
+{
+ auto s = cast(resource);
+ Q_ASSERT(client == *s->client);
+ s->q_func()->maximizedChanged(false);
+}
+
+void XdgSurfaceV5Interface::Private::setFullscreenCallback(wl_client *client, wl_resource *resource, wl_resource *output)
+{
+ auto s = cast(resource);
+ Q_ASSERT(client == *s->client);
+ OutputInterface *o = nullptr;
+ if (output) {
+ o = OutputInterface::get(output);
+ }
+ s->q_func()->fullscreenChanged(true, o);
+}
+
+void XdgSurfaceV5Interface::Private::unsetFullscreenCallback(wl_client *client, wl_resource *resource)
+{
+ auto s = cast(resource);
+ Q_ASSERT(client == *s->client);
+ s->q_func()->fullscreenChanged(false, nullptr);
+}
+
+void XdgSurfaceV5Interface::Private::setMinimizedCallback(wl_client *client, wl_resource *resource)
+{
+ auto s = cast(resource);
+ Q_ASSERT(client == *s->client);
+ s->q_func()->minimizeRequested();
+}
+
+XdgSurfaceV5Interface::Private::Private(XdgSurfaceV5Interface *q, XdgShellV5Interface *c, SurfaceInterface *surface, wl_resource *parentResource)
+ : XdgShellSurfaceInterface::Private(XdgShellInterfaceVersion::UnstableV5, q, c, surface, parentResource, &xdg_surface_interface, &s_interface)
+{
+}
+
+XdgSurfaceV5Interface::Private::~Private() = default;
+
+void XdgSurfaceV5Interface::Private::close()
+{
+ xdg_surface_send_close(resource);
+ client->flush();
+}
+
+quint32 XdgSurfaceV5Interface::Private::configure(States states, const QSize &size)
+{
+ if (!resource) {
+ return 0;
+ }
+ const quint32 serial = global->display()->nextSerial();
+ wl_array state;
+ wl_array_init(&state);
+ if (states.testFlag(State::Maximized)) {
+ uint32_t *s = reinterpret_cast(wl_array_add(&state, sizeof(uint32_t)));
+ *s = XDG_SURFACE_STATE_MAXIMIZED;
+ }
+ if (states.testFlag(State::Fullscreen)) {
+ uint32_t *s = reinterpret_cast(wl_array_add(&state, sizeof(uint32_t)));
+ *s = XDG_SURFACE_STATE_FULLSCREEN;
+ }
+ if (states.testFlag(State::Resizing)) {
+ uint32_t *s = reinterpret_cast(wl_array_add(&state, sizeof(uint32_t)));
+ *s = XDG_SURFACE_STATE_RESIZING;
+ }
+ if (states.testFlag(State::Activated)) {
+ uint32_t *s = reinterpret_cast(wl_array_add(&state, sizeof(uint32_t)));
+ *s = XDG_SURFACE_STATE_ACTIVATED;
+ }
+ configureSerials << serial;
+ xdg_surface_send_configure(resource, size.width(), size.height(), &state, serial);
+ client->flush();
+ wl_array_release(&state);
+
+ return serial;
+}
+
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+const struct xdg_popup_interface XdgPopupV5Interface::Private::s_interface = {
+ resourceDestroyedCallback
+};
+#endif
+
+XdgPopupV5Interface::Private::Private(XdgPopupV5Interface *q, XdgShellV5Interface *c, SurfaceInterface *surface, wl_resource *parentResource)
+ : XdgShellPopupInterface::Private(XdgShellInterfaceVersion::UnstableV5, q, c, surface, parentResource, &xdg_popup_interface, &s_interface)
+{
+}
+
+XdgPopupV5Interface::Private::~Private() = default;
+
+
+void XdgPopupV5Interface::Private::popupDone()
+{
+ if (!resource) {
+ return;
+ }
+ // TODO: dismiss all child popups
+ xdg_popup_send_popup_done(resource);
+ client->flush();
+}
+
+XdgShellV5Interface::XdgShellV5Interface(Display *display, QObject *parent)
+ : XdgShellInterface(new Private(this, display), parent)
+{
+}
+
+XdgShellV5Interface::~XdgShellV5Interface() = default;
+
+XdgSurfaceV5Interface::XdgSurfaceV5Interface(XdgShellV5Interface *parent, SurfaceInterface *surface, wl_resource *parentResource)
+ : KWayland::Server::XdgShellSurfaceInterface(new Private(this, parent, surface, parentResource))
+{
+}
+
+XdgSurfaceV5Interface::~XdgSurfaceV5Interface() = default;
+
+XdgPopupV5Interface::XdgPopupV5Interface(XdgShellV5Interface *parent, SurfaceInterface *surface, wl_resource *parentResource)
+ : XdgShellPopupInterface(new Private(this, parent, surface, parentResource))
+{
+}
+
+XdgPopupV5Interface::~XdgPopupV5Interface() = default;
+
+XdgPopupV5Interface::Private *XdgPopupV5Interface::d_func() const
+{
+ return reinterpret_cast(d.data());
+}
+
+}
+}
+
diff --git a/src/wayland/server/xdgshell_v5_interface_p.h b/src/wayland/server/xdgshell_v5_interface_p.h
new file mode 100644
index 0000000000..a7fd9da871
--- /dev/null
+++ b/src/wayland/server/xdgshell_v5_interface_p.h
@@ -0,0 +1,94 @@
+/****************************************************************************
+Copyright 2016 Martin Gräßlin
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) version 3, or any
+later version accepted by the membership of KDE e.V. (or its
+successor approved by the membership of KDE e.V.), which shall
+act as a proxy defined in Section 6 of version 3 of the license.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library. If not, see .
+****************************************************************************/
+#ifndef KWAYLAND_SERVER_XDGSHELL_V5_INTERFACE_P_H
+#define KWAYLAND_SERVER_XDGSHELL_V5_INTERFACE_P_H
+
+#include "global.h"
+#include "resource.h"
+#include "xdgshell_interface.h"
+
+#include
+
+#include
+
+namespace KWayland
+{
+namespace Server
+{
+
+class Display;
+class OutputInterface;
+class SeatInterface;
+class SurfaceInterface;
+class XdgPopupV5Interface;
+class XdgSurfaceV5Interface;
+template
+class GenericShellSurface;
+
+class XdgShellV5Interface : public XdgShellInterface
+{
+ Q_OBJECT
+public:
+ virtual ~XdgShellV5Interface();
+
+ /**
+ * @returns The XdgSurfaceV5Interface for the @p native resource.
+ **/
+ XdgSurfaceV5Interface *getSurface(wl_resource *native);
+
+private:
+ explicit XdgShellV5Interface(Display *display, QObject *parent = nullptr);
+ friend class Display;
+ class Private;
+ Private *d_func() const;
+};
+
+class XdgSurfaceV5Interface : public XdgShellSurfaceInterface
+{
+ Q_OBJECT
+public:
+ virtual ~XdgSurfaceV5Interface();
+
+private:
+ explicit XdgSurfaceV5Interface(XdgShellV5Interface *parent, SurfaceInterface *surface, wl_resource *parentResource);
+ friend class XdgShellV5Interface;
+
+ class Private;
+};
+
+class XdgPopupV5Interface : public XdgShellPopupInterface
+{
+ Q_OBJECT
+public:
+ virtual ~XdgPopupV5Interface();
+
+private:
+ explicit XdgPopupV5Interface(XdgShellV5Interface *parent, SurfaceInterface *surface, wl_resource *parentResource);
+ friend class XdgShellV5Interface;
+ friend class GenericShellSurface;
+
+ class Private;
+ Private *d_func() const;
+};
+
+}
+}
+
+#endif
diff --git a/src/wayland/tools/mapping.txt b/src/wayland/tools/mapping.txt
index bd2630bc5b..3706952bff 100644
--- a/src/wayland/tools/mapping.txt
+++ b/src/wayland/tools/mapping.txt
@@ -44,3 +44,6 @@ wl_text_input;TextInputUnstableV0
wl_text_input_manager;TextInputManagerUnstableV0
zwp_text_input_v2;TextInputUnstableV2
zwp_text_input_manager_v2;TextInputManagerUnstableV2
+xdg_shell;XdgShellV5
+xdg_surface;XdgSurfaceV5
+xdg_popup;XdgPopupV5
diff --git a/src/wayland/xdgshell_interface.cpp b/src/wayland/xdgshell_interface.cpp
new file mode 100644
index 0000000000..00976780ae
--- /dev/null
+++ b/src/wayland/xdgshell_interface.cpp
@@ -0,0 +1,179 @@
+/****************************************************************************
+Copyright 2016 Martin Gräßlin
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) version 3, or any
+later version accepted by the membership of KDE e.V. (or its
+successor approved by the membership of KDE e.V.), which shall
+act as a proxy defined in Section 6 of version 3 of the license.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library. If not, see .
+****************************************************************************/
+#include "xdgshell_interface_p.h"
+
+namespace KWayland
+{
+namespace Server
+{
+
+XdgShellInterface::Private::Private(XdgShellInterfaceVersion interfaceVersion, XdgShellInterface *q, Display *d, const wl_interface *interface, quint32 version)
+ : Global::Private(d, interface, version)
+ , interfaceVersion(interfaceVersion)
+ , q(q)
+{
+}
+
+XdgShellInterface::XdgShellInterface(Private *d, QObject *parent)
+ : Global(d, parent)
+{
+}
+
+XdgShellInterface::~XdgShellInterface() = default;
+
+XdgShellSurfaceInterface *XdgShellInterface::getSurface(wl_resource *native)
+{
+ Q_UNUSED(native)
+ return nullptr;
+}
+
+XdgShellInterfaceVersion XdgShellInterface::interfaceVersion() const
+{
+ Q_D();
+ return d->interfaceVersion;
+}
+
+XdgShellInterface::Private *XdgShellInterface::d_func() const
+{
+ return reinterpret_cast(d.data());
+}
+
+XdgShellSurfaceInterface::Private::Private(XdgShellInterfaceVersion interfaceVersion, XdgShellSurfaceInterface *q, XdgShellInterface *c, SurfaceInterface *surface, wl_resource *parentResource, const wl_interface *interface, const void *implementation)
+ : Resource::Private(q, c, parentResource, interface, implementation)
+ , GenericShellSurface(q, surface)
+ , interfaceVersion(interfaceVersion)
+{
+}
+
+XdgShellSurfaceInterface::Private::~Private() = default;
+
+XdgShellSurfaceInterface::XdgShellSurfaceInterface(Private *p)
+ : Resource(p)
+{
+}
+
+XdgShellSurfaceInterface::~XdgShellSurfaceInterface() = default;
+
+XdgShellInterfaceVersion XdgShellSurfaceInterface::interfaceVersion() const
+{
+ Q_D();
+ return d->interfaceVersion;
+}
+
+quint32 XdgShellSurfaceInterface::configure(States states, const QSize &size)
+{
+ Q_D();
+ return d->configure(states, size);
+}
+
+bool XdgShellSurfaceInterface::isConfigurePending() const
+{
+ Q_D();
+ return !d->configureSerials.isEmpty();
+}
+
+SurfaceInterface *XdgShellSurfaceInterface::surface() const
+{
+ Q_D();
+ return d->surface;
+}
+
+QString XdgShellSurfaceInterface::title() const
+{
+ Q_D();
+ return d->title;
+}
+
+QByteArray XdgShellSurfaceInterface::windowClass() const
+{
+ Q_D();
+ return d->windowClass;
+}
+
+bool XdgShellSurfaceInterface::isTransient() const
+{
+ Q_D();
+ return !d->parent.isNull();
+}
+
+QPointer XdgShellSurfaceInterface::transientFor() const
+{
+ Q_D();
+ return d->parent;
+}
+
+void XdgShellSurfaceInterface::close()
+{
+ Q_D();
+ d->close();
+}
+
+XdgShellSurfaceInterface::Private *XdgShellSurfaceInterface::d_func() const
+{
+ return reinterpret_cast(d.data());
+}
+
+XdgShellPopupInterface::Private::Private(XdgShellInterfaceVersion interfaceVersion, XdgShellPopupInterface *q, XdgShellInterface *c, SurfaceInterface *surface, wl_resource *parentResource, const wl_interface *interface, const void *implementation)
+ : Resource::Private(q, c, parentResource, interface, implementation)
+ , GenericShellSurface(q, surface)
+ , interfaceVersion(interfaceVersion)
+{
+}
+
+XdgShellPopupInterface::Private::~Private() = default;
+
+XdgShellPopupInterface::XdgShellPopupInterface(Private *p)
+ : Resource(p)
+{
+}
+
+XdgShellPopupInterface::~XdgShellPopupInterface() = default;
+
+SurfaceInterface *XdgShellPopupInterface::surface() const
+{
+ Q_D();
+ return d->surface;
+}
+
+QPointer XdgShellPopupInterface::transientFor() const
+{
+ Q_D();
+ return d->parent;
+}
+
+QPoint XdgShellPopupInterface::transientOffset() const
+{
+ Q_D();
+ return d->transientOffset;
+}
+
+void XdgShellPopupInterface::popupDone()
+{
+ Q_D();
+ return d->popupDone();
+}
+
+XdgShellPopupInterface::Private *XdgShellPopupInterface::d_func() const
+{
+ return reinterpret_cast(d.data());
+}
+
+}
+}
diff --git a/src/wayland/xdgshell_interface.h b/src/wayland/xdgshell_interface.h
new file mode 100644
index 0000000000..90518db67f
--- /dev/null
+++ b/src/wayland/xdgshell_interface.h
@@ -0,0 +1,304 @@
+/****************************************************************************
+Copyright 2016 Martin Gräßlin
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) version 3, or any
+later version accepted by the membership of KDE e.V. (or its
+successor approved by the membership of KDE e.V.), which shall
+act as a proxy defined in Section 6 of version 3 of the license.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library. If not, see .
+****************************************************************************/
+#ifndef KWAYLAND_SERVER_XDGSHELL_INTERFACE_H
+#define KWAYLAND_SERVER_XDGSHELL_INTERFACE_H
+
+#include "global.h"
+#include "resource.h"
+
+#include
+
+#include
+
+namespace KWayland
+{
+namespace Server
+{
+
+class OutputInterface;
+class SeatInterface;
+class SurfaceInterface;
+class XdgShellPopupInterface;
+class XdgShellSurfaceInterface;
+template
+class GenericShellSurface;
+
+/**
+ * Enum describing the different InterfaceVersion encapsulated in this implementation.
+ *
+ * @since 5.25
+ **/
+enum class XdgShellInterfaceVersion
+{
+ /**
+ * xdg_shell (unstable v5)
+ **/
+ UnstableV5
+};
+
+/**
+ *
+ * @since 5.25
+ **/
+class KWAYLANDSERVER_EXPORT XdgShellInterface : public Global
+{
+ Q_OBJECT
+public:
+ virtual ~XdgShellInterface();
+
+ /**
+ * @returns The interface version used by this XdgShellInterface
+ **/
+ XdgShellInterfaceVersion interfaceVersion() const;
+
+ /**
+ * @returns The XdgShellSurfaceInterface for the @p native resource.
+ **/
+ XdgShellSurfaceInterface *getSurface(wl_resource *native);
+
+Q_SIGNALS:
+ void surfaceCreated(KWayland::Server::XdgShellSurfaceInterface *surface);
+ /**
+ * Emitted whenever a new popup got created.
+ *
+ * A popup only gets created in response to an action on the @p seat.
+ *
+ * @param surface The popup xdg shell surface which got created
+ * @param seat The seat on which an action triggered the popup
+ * @param serial The serial of the action on the seat
+ **/
+ void popupCreated(KWayland::Server::XdgShellPopupInterface *surface, KWayland::Server::SeatInterface *seat, quint32 serial);
+
+protected:
+ class Private;
+ explicit XdgShellInterface(Private *d, QObject *parent = nullptr);
+
+private:
+ Private *d_func() const;
+};
+
+
+/**
+ *
+ * @since 5.25
+ **/
+class KWAYLANDSERVER_EXPORT XdgShellSurfaceInterface : public Resource
+{
+ Q_OBJECT
+public:
+ virtual ~XdgShellSurfaceInterface();
+
+ /**
+ * @returns The interface version used by this XdgShellSurfaceInterface
+ **/
+ XdgShellInterfaceVersion interfaceVersion() const;
+
+ /**
+ * States the Surface can be in
+ **/
+ enum class State {
+ /**
+ * The Surface is maximized.
+ **/
+ Maximized = 1 << 0,
+ /**
+ * The Surface is fullscreen.
+ **/
+ Fullscreen = 1 << 1,
+ /**
+ * The Surface is currently being resized by the Compositor.
+ **/
+ Resizing = 1 << 2,
+ /**
+ * The Surface is considered active. Does not imply keyboard focus.
+ **/
+ Activated = 1 << 3
+ };
+ Q_DECLARE_FLAGS(States, State)
+
+ /**
+ * Sends a configure event to the Surface.
+ * This tells the Surface the current @p states it is in and the @p size it should have.
+ * If @p size has width and height at @c 0, the Surface can choose the size.
+ *
+ * The Surface acknowledges the configure event with @link{configureAcknowledged}.
+ *
+ * @param states The states the surface is in
+ * @param size The requested size
+ * @returns The serial of the configure event
+ * @see configureAcknowledged
+ * @see isConfigurePending
+ **/
+ quint32 configure(States states, const QSize &size = QSize(0, 0));
+
+ /**
+ * @returns @c true if there is a not yet acknowledged configure event.
+ * @see configure
+ * @see configureAcknowledged
+ **/
+ bool isConfigurePending() const;
+
+ /**
+ * @return The SurfaceInterface this XdgSurfaceV5Interface got created for.
+ **/
+ SurfaceInterface *surface() const;
+
+ /**
+ * @returns The title of this surface.
+ * @see titleChanged
+ **/
+ QString title() const;
+ QByteArray windowClass() const;
+
+ /**
+ * @returns Whether this Surface is a transient for another Surface, that is it has a parent.
+ * @see transientFor
+ **/
+ bool isTransient() const;
+ /**
+ * @returns the parent surface if the surface is a transient for another surface
+ * @see isTransient
+ **/
+ QPointer transientFor() const;
+
+ /**
+ * Request the client to close the window.
+ **/
+ void close();
+
+Q_SIGNALS:
+ /**
+ * Emitted whenever the title changes.
+ *
+ * @see title
+ **/
+ void titleChanged(const QString&);
+ /**
+ * Emitted whenever the window class changes.
+ *
+ * @see windowClass
+ **/
+ void windowClassChanged(const QByteArray&);
+ /**
+ * The surface requested a window move.
+ *
+ * @param seat The SeatInterface on which the surface requested the move
+ * @param serial The serial of the implicit mouse grab which triggered the move
+ **/
+ void moveRequested(KWayland::Server::SeatInterface *seat, quint32 serial);
+ /**
+ * The surface requested a window resize.
+ *
+ * @param seat The SeatInterface on which the surface requested the resize
+ * @param serial The serial of the implicit mouse grab which triggered the resize
+ * @param edges A hint which edges are involved in the resize
+ **/
+ void resizeRequested(KWayland::Server::SeatInterface *seat, quint32 serial, Qt::Edges edges);
+ void windowMenuRequested(KWayland::Server::SeatInterface *seat, quint32 serial, const QPoint &surfacePos);
+ /**
+ * The surface requested a change of maximized state.
+ * @param maximized Whether the window wants to be maximized
+ **/
+ void maximizedChanged(bool maximized);
+ /**
+ * The surface requested a change of fullscreen state
+ * @param fullscreen Whether the window wants to be fullscreen
+ * @param output An optional output hint on which the window wants to be fullscreen
+ **/
+ void fullscreenChanged(bool fullscreen, KWayland::Server::OutputInterface *output);
+ /**
+ * The surface requested to be minimized.
+ **/
+ void minimizeRequested();
+ /**
+ * A configure event with @p serial got acknowledged.
+ * @see configure
+ **/
+ void configureAcknowledged(quint32 serial);
+ /**
+ * Emitted whenever the parent surface changes.
+ * @see isTransient
+ * @see transientFor
+ **/
+ void transientForChanged();
+
+protected:
+ class Private;
+ explicit XdgShellSurfaceInterface(Private *p);
+
+private:
+ Private *d_func() const;
+ friend class GenericShellSurface;
+};
+
+/**
+ *
+ * @since 5.25
+ **/
+class KWAYLANDSERVER_EXPORT XdgShellPopupInterface : public Resource
+{
+ Q_OBJECT
+public:
+ virtual ~XdgShellPopupInterface();
+
+ /**
+ * @return The SurfaceInterface this XdgShellPopupInterface got created for.
+ **/
+ SurfaceInterface *surface() const;
+
+ /**
+ * @returns the parent surface.
+ * @see transientOffset
+ **/
+ QPointer transientFor() const;
+ /**
+ * The offset of the Surface in the coordinate system of the SurfaceInterface this surface is a transient for.
+ *
+ * @returns offset in parent coordinate system.
+ * @see transientFor
+ **/
+ QPoint transientOffset() const;
+
+ /**
+ * Dismiss this popup. This indicates to the client that it should destroy this popup.
+ * The Compositor can invoke this method when e.g. the user clicked outside the popup
+ * to dismiss it.
+ **/
+ void popupDone();
+
+protected:
+ class Private;
+ explicit XdgShellPopupInterface(Private *p);
+
+private:
+ friend class GenericShellSurface;
+
+ Private *d_func() const;
+};
+
+}
+}
+
+Q_DECLARE_METATYPE(KWayland::Server::XdgShellSurfaceInterface *)
+Q_DECLARE_METATYPE(KWayland::Server::XdgShellPopupInterface *)
+Q_DECLARE_METATYPE(KWayland::Server::XdgShellSurfaceInterface::State)
+Q_DECLARE_METATYPE(KWayland::Server::XdgShellSurfaceInterface::States)
+
+#endif
diff --git a/src/wayland/xdgshell_interface_p.h b/src/wayland/xdgshell_interface_p.h
new file mode 100644
index 0000000000..a69134b9c6
--- /dev/null
+++ b/src/wayland/xdgshell_interface_p.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+Copyright 2016 Martin Gräßlin
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) version 3, or any
+later version accepted by the membership of KDE e.V. (or its
+successor approved by the membership of KDE e.V.), which shall
+act as a proxy defined in Section 6 of version 3 of the license.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library. If not, see .
+****************************************************************************/
+#ifndef KWAYLAND_SERVER_XDGSHELL_INTERFACE_P_H
+#define KWAYLAND_SERVER_XDGSHELL_INTERFACE_P_H
+#include "xdgshell_interface.h"
+#include "global_p.h"
+#include "generic_shell_surface_p.h"
+#include "resource_p.h"
+
+namespace KWayland
+{
+namespace Server
+{
+
+class XdgShellInterface::Private : public Global::Private
+{
+public:
+ XdgShellInterfaceVersion interfaceVersion;
+
+protected:
+ Private(XdgShellInterfaceVersion interfaceVersion, XdgShellInterface *q, Display *d, const wl_interface *interface, quint32 version);
+ XdgShellInterface *q;
+};
+
+class XdgShellSurfaceInterface::Private : public Resource::Private, public GenericShellSurface
+{
+public:
+ virtual ~Private();
+
+ virtual void close() = 0;
+ virtual quint32 configure(States states, const QSize &size) = 0;
+
+ XdgShellSurfaceInterface *q_func() {
+ return reinterpret_cast(q);
+ }
+
+ QVector configureSerials;
+ QPointer parent;
+ XdgShellInterfaceVersion interfaceVersion;
+
+protected:
+ Private(XdgShellInterfaceVersion interfaceVersion, XdgShellSurfaceInterface *q, XdgShellInterface *c, SurfaceInterface *surface, wl_resource *parentResource, const wl_interface *interface, const void *implementation);
+
+};
+
+class XdgShellPopupInterface::Private : public Resource::Private, public GenericShellSurface
+{
+public:
+ virtual ~Private();
+ virtual void popupDone() = 0;
+
+ XdgShellPopupInterface *q_func() {
+ return reinterpret_cast(q);
+ }
+
+ QPointer parent;
+ QPoint transientOffset;
+ XdgShellInterfaceVersion interfaceVersion;
+
+protected:
+ Private(XdgShellInterfaceVersion interfaceVersion, XdgShellPopupInterface *q, XdgShellInterface *c, SurfaceInterface *surface, wl_resource *parentResource, const wl_interface *interface, const void *implementation);
+
+};
+
+}
+}
+
+#endif