Add support for xdg-shell

Summary:
This change introduces support for the unstable xdg-shell interface in
the server. The implementation is based on version 5 of the unstable
interface. This is the version used by toolkits like e.g. GTK.

There is also a version 6 of the protocol under development which is
incompatible. This makes it difficult to implement it in a backward
compatible way.

Because of that the implementation is a little bit different to other
interfaces and inspired by the TextInput interfaces:
On client side an XdgShell class is exposed which does not represent
it directly. Instead it delegates everything to an XdgShellUnstableV5
implementation. For the Surface/Popup the same is done.

In the Registry it's possible to create an XdgShell and it accepts
the XdgShellUnstableV5 and in future will accept XdgUnstableV6, etc.

On server side it also follows the approach from TextInput. That is
there is a version enum which gets passed to the factory method in
Display. It currently supports only V5, but in future can be extended
for V6. As there is lots of similar code between wl_shell, xdg_shell
and in future xdg_shell_unstable_v6 a templated GenericShellInterface
class is added which combines the common parts.

Reviewers: #plasma_on_wayland

Subscribers: plasma-devel

Tags: #plasma_on_wayland

Differential Revision: https://phabricator.kde.org/D2102
This commit is contained in:
Martin Gräßlin 2016-04-21 12:56:02 +02:00
parent e0716c2306
commit 53f27feee7
15 changed files with 1995 additions and 62 deletions

View file

@ -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
)

View file

@ -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)

View file

@ -30,6 +30,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
#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 <http://www.gnu.org/licenses/>.
#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 <wayland-client-protocol.h>
#include <wayland-dpms-client-protocol.h>
#include <wayland-server-decoration-client-protocol.h>
#include <wayland-text-input-v0-client-protocol.h>
#include <wayland-text-input-v2-client-protocol.h>
#include <wayland-xdg-shell-v5-client-protocol.h>
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()

View file

@ -0,0 +1,649 @@
/********************************************************************
Copyright 2016 Martin Gräßlin <mgraesslin@kde.org>
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 <http://www.gnu.org/licenses/>.
*********************************************************************/
// Qt
#include <QtTest/QtTest>
// 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(&registry, &Registry::interfacesAnnounced);
QVERIFY(interfacesAnnouncedSpy.isValid());
QSignalSpy interfaceAnnouncedSpy(&registry, &Registry::interfaceAnnounced);
QVERIFY(interfaceAnnouncedSpy.isValid());
QSignalSpy outputAnnouncedSpy(&registry, &Registry::outputAnnounced);
QVERIFY(outputAnnouncedSpy.isValid());
QSignalSpy xdgShellAnnouncedSpy(&registry, &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<quint32>(), outputAnnouncedSpy.first().at(1).value<quint32>(), this);
m_output2 = registry.createOutput(outputAnnouncedSpy.last().at(0).value<quint32>(), outputAnnouncedSpy.last().at(1).value<quint32>(), 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> surface(m_compositor->createSurface());
QVERIFY(!surface.isNull());
QVERIFY(surfaceCreatedSpy.wait());
auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface*>();
QVERIFY(serverSurface);
// create shell surface
QScopedPointer<XdgShellSurface> xdgSurface(m_xdgShell->createSurface(surface.data()));
QVERIFY(!xdgSurface.isNull());
QVERIFY(xdgSurfaceCreatedSpy.wait());
// verify base things
auto serverXdgSurface = xdgSurfaceCreatedSpy.first().first().value<XdgShellSurfaceInterface*>();
QVERIFY(serverXdgSurface);
QCOMPARE(serverXdgSurface->isConfigurePending(), false);
QCOMPARE(serverXdgSurface->title(), QString());
QCOMPARE(serverXdgSurface->windowClass(), QByteArray());
QCOMPARE(serverXdgSurface->isTransient(), false);
QCOMPARE(serverXdgSurface->transientFor(), QPointer<XdgShellSurfaceInterface>());
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> surface(m_compositor->createSurface()); \
QScopedPointer<XdgShellSurface> xdgSurface(m_xdgShell->createSurface(surface.data())); \
QCOMPARE(xdgSurface->size(), QSize()); \
QVERIFY(xdgSurfaceCreatedSpy.wait()); \
auto serverXdgSurface = xdgSurfaceCreatedSpy.first().first().value<XdgShellSurfaceInterface*>(); \
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<OutputInterface*>();
// this test verifies going to/from fullscreen
QSignalSpy xdgSurfaceCreatedSpy(m_xdgShellInterface, &XdgShellInterface::surfaceCreated);
QVERIFY(xdgSurfaceCreatedSpy.isValid());
QScopedPointer<Surface> surface(m_compositor->createSurface());
QScopedPointer<XdgShellSurface> xdgSurface(m_xdgShell->createSurface(surface.data()));
QVERIFY(xdgSurfaceCreatedSpy.wait());
auto serverXdgSurface = xdgSurfaceCreatedSpy.first().first().value<XdgShellSurfaceInterface*>();
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<OutputInterface*>());
// unset
xdgSurface->setFullscreen(false);
QVERIFY(fullscreenSpy.wait());
QCOMPARE(fullscreenSpy.count(), 2);
QCOMPARE(fullscreenSpy.last().at(0).toBool(), false);
QVERIFY(!fullscreenSpy.last().at(1).value<OutputInterface*>());
// 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<OutputInterface*>(), 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<OutputInterface*>(), m_o2Interface);
}
void XdgShellTest::testShowWindowMenu()
{
qRegisterMetaType<SeatInterface*>();
// 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<SeatInterface*>(), m_seatInterface);
QCOMPARE(windowMenuSpy.first().at(1).value<quint32>(), 20u);
QCOMPARE(windowMenuSpy.first().at(2).toPoint(), QPoint(30, 40));
}
void XdgShellTest::testMove()
{
qRegisterMetaType<SeatInterface*>();
// 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<SeatInterface*>(), m_seatInterface);
QCOMPARE(moveSpy.first().at(1).value<quint32>(), 50u);
}
void XdgShellTest::testResize_data()
{
QTest::addColumn<Qt::Edges>("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<SeatInterface*>();
// 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<SeatInterface*>(), m_seatInterface);
QCOMPARE(resizeSpy.first().at(1).value<quint32>(), 60u);
QCOMPARE(resizeSpy.first().at(2).value<Qt::Edges>(), edges);
}
void XdgShellTest::testTransient()
{
// this test verifies that setting the transient for works
SURFACE
QScopedPointer<Surface> surface2(m_compositor->createSurface());
QScopedPointer<XdgShellSurface> xdgSurface2(m_xdgShell->createSurface(surface2.data()));
QVERIFY(xdgSurfaceCreatedSpy.wait());
auto serverXdgSurface2 = xdgSurfaceCreatedSpy.last().first().value<XdgShellSurfaceInterface*>();
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<XdgShellSurfaceInterface::States>("serverStates");
QTest::addColumn<XdgShellSurface::States>("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<XdgShellSurface::States>();
// 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<XdgShellSurface::States>(), "clientStates");
QCOMPARE(configureSpy.first().at(2).value<quint32>(), m_display->serial());
QSignalSpy ackSpy(serverXdgSurface, &XdgShellSurfaceInterface::configureAcknowledged);
QVERIFY(ackSpy.isValid());
xdgSurface->ackConfigure(configureSpy.first().at(2).value<quint32>());
QVERIFY(ackSpy.wait());
QCOMPARE(ackSpy.count(), 1);
QCOMPARE(ackSpy.first().first().value<quint32>(), configureSpy.first().at(2).value<quint32>());
}
void XdgShellTest::testConfigureMultipleAcks()
{
qRegisterMetaType<XdgShellSurface::States>();
// 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>(), XdgShellSurface::States());
QCOMPARE(configureSpy.at(0).at(2).value<quint32>(), serial1);
QCOMPARE(configureSpy.at(1).at(0).toSize(), QSize(20, 30));
QCOMPARE(configureSpy.at(1).at(1).value<XdgShellSurface::States>(), XdgShellSurface::States());
QCOMPARE(configureSpy.at(1).at(2).value<quint32>(), serial2);
QCOMPARE(configureSpy.at(2).at(0).toSize(), QSize(30, 40));
QCOMPARE(configureSpy.at(2).at(1).value<XdgShellSurface::States>(), XdgShellSurface::States());
QCOMPARE(configureSpy.at(2).at(2).value<quint32>(), 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<quint32>(), serial1);
QCOMPARE(ackSpy.at(1).first().value<quint32>(), serial2);
QCOMPARE(ackSpy.at(2).first().value<quint32>(), 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<Surface> popupSurface(m_compositor->createSurface());
QVERIFY(surfaceCreatedSpy.wait());
// TODO: proper serial
QScopedPointer<XdgShellPopup> 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<SeatInterface*>(), m_seatInterface);
QCOMPARE(xdgPopupSpy.first().at(2).value<quint32>(), 120u);
auto serverXdgPopup = xdgPopupSpy.first().first().value<XdgShellPopupInterface*>();
QVERIFY(serverXdgPopup);
QCOMPARE(serverXdgPopup->surface(), surfaceCreatedSpy.first().first().value<SurfaceInterface*>());
QCOMPARE(serverXdgPopup->transientFor().data(), serverXdgSurface->surface());
QCOMPARE(serverXdgPopup->transientOffset(), QPoint(10, 20));
// now also a popup for the popup
QScopedPointer<Surface> popup2Surface(m_compositor->createSurface());
QScopedPointer<XdgShellPopup> 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<SeatInterface*>(), m_seatInterface);
QCOMPARE(xdgPopupSpy.last().at(2).value<quint32>(), 121u);
auto serverXdgPopup2 = xdgPopupSpy.last().first().value<XdgShellPopupInterface*>();
QVERIFY(serverXdgPopup2);
QCOMPARE(serverXdgPopup2->surface(), surfaceCreatedSpy.last().first().value<SurfaceInterface*>());
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"

View file

@ -40,6 +40,7 @@ License along with this library. If not, see <http://www.gnu.org/licenses/>.
#include "shell_interface.h"
#include "subcompositor_interface.h"
#include "textinput_interface_p.h"
#include "xdgshell_v5_interface_p.h"
#include <QCoreApplication>
#include <QDebug>
@ -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);

View file

@ -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.

View file

@ -0,0 +1,129 @@
/********************************************************************
Copyright 2016 Martin Gräßlin <mgraesslin@kde.org>
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 <http://www.gnu.org/licenses/>.
*********************************************************************/
#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 <wayland-server.h>
namespace KWayland
{
namespace Server
{
template <class T>
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 <typename U>
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<typename T::Private*>(wl_resource_get_user_data(resource));
}
T *shellSurface;
};
template <class T>
void GenericShellSurface<T>::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 <class T>
void GenericShellSurface<T>::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 <class T>
void GenericShellSurface<T>::setTitle(const QString &t)
{
if (title == t) {
return;
}
title = t;
Q_Q(T);
emit q->titleChanged(title);
}
template <class T>
void GenericShellSurface<T>::setWindowClass(const QByteArray &wc)
{
if (windowClass == wc) {
return;
}
windowClass = wc;
Q_Q(T);
emit q->windowClassChanged(windowClass);
}
template <class T>
void GenericShellSurface<T>::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 <typename T>
Qt::Edges edgesToQtEdges(T edges);
}
template <class T>
template <typename U>
void GenericShellSurface<T>::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

View file

@ -18,6 +18,7 @@ You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>.
*********************************************************************/
#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<ShellSurfaceInterface>
{
public:
Private(ShellSurfaceInterface *q, ShellInterface *shell, SurfaceInterface *surface, wl_resource *parentResource);
void ping();
SurfaceInterface *surface;
QString title;
QByteArray windowClass;
QScopedPointer<QTimer> 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<ShellSurfaceInterface *>(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<ShellSurfaceInterface *>(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<KWayland::Server::ShellSurfaceInterface>(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<wl_shell_surface_resize>,
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<Private>(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<Private>(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<Private>(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<Private>(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;

View file

@ -40,6 +40,8 @@ class Display;
class SeatInterface;
class SurfaceInterface;
class ShellSurfaceInterface;
template <typename T>
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<ShellSurfaceInterface>;
class Private;
Private *d_func() const;
};

View file

@ -0,0 +1,475 @@
/****************************************************************************
Copyright 2016 Martin Gräßlin <mgraesslin@kde.org>
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 <http://www.gnu.org/licenses/>.
****************************************************************************/
#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 <wayland-xdg-shell-v5-server-protocol.h>
namespace KWayland
{
namespace Server
{
class XdgShellV5Interface::Private : public XdgShellInterface::Private
{
public:
Private(XdgShellV5Interface *q, Display *d);
QVector<XdgSurfaceV5Interface*> 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<Private*>(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<XdgPopupV5Interface *>(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<SurfaceInterface>(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<Private*>(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<XdgSurfaceV5Interface *>(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<xdg_surface_resize_edge>,
ackConfigureCallback,
setWindowGeometryCallback,
setMaximizedCallback,
unsetMaximizedCallback,
setFullscreenCallback,
unsetFullscreenCallback,
setMinimizedCallback
};
#endif
void XdgSurfaceV5Interface::Private::setParentCallback(wl_client *client, wl_resource *resource, wl_resource *parent)
{
auto s = cast<Private>(resource);
Q_ASSERT(client == *s->client);
auto parentSurface = static_cast<XdgShellV5Interface*>(s->q->global())->getSurface(parent);
if (s->parent.data() != parentSurface) {
s->parent = QPointer<XdgSurfaceV5Interface>(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<Private>(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<Private>(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<Private>(resource);
Q_ASSERT(client == *s->client);
s->q_func()->maximizedChanged(true);
}
void XdgSurfaceV5Interface::Private::unsetMaximizedCallback(wl_client *client, wl_resource *resource)
{
auto s = cast<Private>(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<Private>(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<Private>(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<Private>(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<uint32_t*>(wl_array_add(&state, sizeof(uint32_t)));
*s = XDG_SURFACE_STATE_MAXIMIZED;
}
if (states.testFlag(State::Fullscreen)) {
uint32_t *s = reinterpret_cast<uint32_t*>(wl_array_add(&state, sizeof(uint32_t)));
*s = XDG_SURFACE_STATE_FULLSCREEN;
}
if (states.testFlag(State::Resizing)) {
uint32_t *s = reinterpret_cast<uint32_t*>(wl_array_add(&state, sizeof(uint32_t)));
*s = XDG_SURFACE_STATE_RESIZING;
}
if (states.testFlag(State::Activated)) {
uint32_t *s = reinterpret_cast<uint32_t*>(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<Private*>(d.data());
}
}
}

View file

@ -0,0 +1,94 @@
/****************************************************************************
Copyright 2016 Martin Gräßlin <mgraesslin@kde.org>
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 <http://www.gnu.org/licenses/>.
****************************************************************************/
#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 <KWayland/Server/kwaylandserver_export.h>
#include <QSize>
namespace KWayland
{
namespace Server
{
class Display;
class OutputInterface;
class SeatInterface;
class SurfaceInterface;
class XdgPopupV5Interface;
class XdgSurfaceV5Interface;
template <typename T>
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<XdgPopupV5Interface>;
class Private;
Private *d_func() const;
};
}
}
#endif

View file

@ -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

View file

@ -0,0 +1,179 @@
/****************************************************************************
Copyright 2016 Martin Gräßlin <mgraesslin@kde.org>
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 <http://www.gnu.org/licenses/>.
****************************************************************************/
#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<Private*>(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<XdgShellSurfaceInterface>(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> XdgShellSurfaceInterface::transientFor() const
{
Q_D();
return d->parent;
}
void XdgShellSurfaceInterface::close()
{
Q_D();
d->close();
}
XdgShellSurfaceInterface::Private *XdgShellSurfaceInterface::d_func() const
{
return reinterpret_cast<Private*>(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<XdgShellPopupInterface>(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<SurfaceInterface> 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<Private*>(d.data());
}
}
}

View file

@ -0,0 +1,304 @@
/****************************************************************************
Copyright 2016 Martin Gräßlin <mgraesslin@kde.org>
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 <http://www.gnu.org/licenses/>.
****************************************************************************/
#ifndef KWAYLAND_SERVER_XDGSHELL_INTERFACE_H
#define KWAYLAND_SERVER_XDGSHELL_INTERFACE_H
#include "global.h"
#include "resource.h"
#include <QSize>
#include <KWayland/Server/kwaylandserver_export.h>
namespace KWayland
{
namespace Server
{
class OutputInterface;
class SeatInterface;
class SurfaceInterface;
class XdgShellPopupInterface;
class XdgShellSurfaceInterface;
template <typename T>
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<XdgShellSurfaceInterface> 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<XdgShellSurfaceInterface>;
};
/**
*
* @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<SurfaceInterface> 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<XdgShellPopupInterface>;
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

View file

@ -0,0 +1,85 @@
/****************************************************************************
Copyright 2016 Martin Gräßlin <mgraesslin@kde.org>
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 <http://www.gnu.org/licenses/>.
****************************************************************************/
#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<XdgShellSurfaceInterface>
{
public:
virtual ~Private();
virtual void close() = 0;
virtual quint32 configure(States states, const QSize &size) = 0;
XdgShellSurfaceInterface *q_func() {
return reinterpret_cast<XdgShellSurfaceInterface *>(q);
}
QVector<quint32> configureSerials;
QPointer<XdgShellSurfaceInterface> 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<XdgShellPopupInterface>
{
public:
virtual ~Private();
virtual void popupDone() = 0;
XdgShellPopupInterface *q_func() {
return reinterpret_cast<XdgShellPopupInterface *>(q);
}
QPointer<SurfaceInterface> 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