diff --git a/autotests/client/test_shm_pool.cpp b/autotests/client/test_shm_pool.cpp index 2aa1be9..2f544f4 100644 --- a/autotests/client/test_shm_pool.cpp +++ b/autotests/client/test_shm_pool.cpp @@ -1,234 +1,232 @@ /* SPDX-FileCopyrightText: 2014 Martin Gräßlin SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ // Qt #include #include // KWin #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/surface.h" #include "../../src/client/registry.h" #include "../../src/client/shm_pool.h" #include "../../src/server/buffer_interface.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/display.h" #include "../../src/server/surface_interface.h" class TestShmPool : public QObject { Q_OBJECT public: explicit TestShmPool(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testCreateBufferNullImage(); void testCreateBufferNullSize(); void testCreateBufferInvalidSize(); void testCreateBufferFromImage(); void testCreateBufferFromImageWithAlpha(); void testCreateBufferFromData(); void testReuseBuffer(); void testDestroy(); private: KWayland::Server::Display *m_display; - KWayland::Server::CompositorInterface *m_compositorInterface; KWayland::Client::ConnectionThread *m_connection; KWayland::Client::Compositor *m_compositor; KWayland::Client::ShmPool *m_shmPool; QThread *m_thread; }; static const QString s_socketName = QStringLiteral("kwin-test-wayland-surface-0"); TestShmPool::TestShmPool(QObject *parent) : QObject(parent) , m_display(nullptr) - , m_compositorInterface(nullptr) , m_connection(nullptr) , m_compositor(nullptr) , m_shmPool(nullptr) , m_thread(nullptr) { } void TestShmPool::init() { using namespace KWayland::Server; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, SIGNAL(connected())); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); KWayland::Client::Registry registry; QSignalSpy shmSpy(®istry, SIGNAL(shmAnnounced(quint32,quint32))); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); // here we need a shm pool m_display->createShm(); QVERIFY(shmSpy.wait()); m_shmPool = registry.createShmPool(shmSpy.first().first().value(), shmSpy.first().last().value(), this); } void TestShmPool::cleanup() { if (m_compositor) { delete m_compositor; m_compositor = nullptr; } if (m_shmPool) { delete m_shmPool; m_shmPool = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } delete m_connection; m_connection = nullptr; delete m_display; m_display = nullptr; } void TestShmPool::testCreateBufferNullImage() { QVERIFY(m_shmPool->isValid()); QImage img; QVERIFY(img.isNull()); QVERIFY(!m_shmPool->createBuffer(img)); } void TestShmPool::testCreateBufferNullSize() { QVERIFY(m_shmPool->isValid()); QSize size(0, 0); QVERIFY(size.isNull()); QVERIFY(!m_shmPool->createBuffer(size, 0, nullptr)); } void TestShmPool::testCreateBufferInvalidSize() { QVERIFY(m_shmPool->isValid()); QSize size; QVERIFY(!size.isValid()); QVERIFY(!m_shmPool->createBuffer(size, 0, nullptr)); } void TestShmPool::testCreateBufferFromImage() { QVERIFY(m_shmPool->isValid()); QImage img(24, 24, QImage::Format_RGB32); img.fill(Qt::black); QVERIFY(!img.isNull()); auto buffer = m_shmPool->createBuffer(img).toStrongRef(); QVERIFY(buffer); QCOMPARE(buffer->size(), img.size()); QImage img2(buffer->address(), img.width(), img.height(), QImage::Format_RGB32); QCOMPARE(img2, img); } void TestShmPool::testCreateBufferFromImageWithAlpha() { QVERIFY(m_shmPool->isValid()); QImage img(24, 24, QImage::Format_ARGB32_Premultiplied); img.fill(QColor(255,0,0,100)); //red with alpha QVERIFY(!img.isNull()); auto buffer = m_shmPool->createBuffer(img).toStrongRef(); QVERIFY(buffer); QCOMPARE(buffer->size(), img.size()); QImage img2(buffer->address(), img.width(), img.height(), QImage::Format_ARGB32_Premultiplied); QCOMPARE(img2, img); } void TestShmPool::testCreateBufferFromData() { QVERIFY(m_shmPool->isValid()); QImage img(24, 24, QImage::Format_ARGB32_Premultiplied); img.fill(Qt::black); QVERIFY(!img.isNull()); auto buffer = m_shmPool->createBuffer(img.size(), img.bytesPerLine(), img.constBits()).toStrongRef(); QVERIFY(buffer); QCOMPARE(buffer->size(), img.size()); QImage img2(buffer->address(), img.width(), img.height(), QImage::Format_ARGB32_Premultiplied); QCOMPARE(img2, img); } void TestShmPool::testReuseBuffer() { QVERIFY(m_shmPool->isValid()); QImage img(24, 24, QImage::Format_ARGB32_Premultiplied); img.fill(Qt::black); QVERIFY(!img.isNull()); auto buffer = m_shmPool->createBuffer(img).toStrongRef(); QVERIFY(buffer); buffer->setReleased(true); buffer->setUsed(false); // same image should get the same buffer auto buffer2 = m_shmPool->createBuffer(img).toStrongRef(); QCOMPARE(buffer, buffer2); buffer2->setReleased(true); buffer2->setUsed(false); // image with different size should get us a new buffer auto buffer3 = m_shmPool->getBuffer(QSize(10, 10), 8); QVERIFY(buffer3 != buffer2); // image with a different format should get us a new buffer QImage img2(24, 24, QImage::Format_RGB32); img2.fill(Qt::black); QVERIFY(!img2.isNull()); auto buffer4 = m_shmPool->createBuffer(img2).toStrongRef(); QVERIFY(buffer4); QVERIFY(buffer4 != buffer2); QVERIFY(buffer4 != buffer3); } void TestShmPool::testDestroy() { using namespace KWayland::Client; connect(m_connection, &ConnectionThread::connectionDied, m_shmPool, &ShmPool::destroy); QVERIFY(m_shmPool->isValid()); // let's create one Buffer m_shmPool->getBuffer(QSize(10, 10), 8); QSignalSpy connectionDiedSpy(m_connection, SIGNAL(connectionDied())); QVERIFY(connectionDiedSpy.isValid()); delete m_display; m_display = nullptr; QVERIFY(connectionDiedSpy.wait()); // now the pool should be destroyed; QVERIFY(!m_shmPool->isValid()); // calling destroy again should not fail m_shmPool->destroy(); } QTEST_GUILESS_MAIN(TestShmPool) #include "test_shm_pool.moc" diff --git a/autotests/client/test_wayland_outputmanagement.cpp b/autotests/client/test_wayland_outputmanagement.cpp index 6790ac6..d32644f 100644 --- a/autotests/client/test_wayland_outputmanagement.cpp +++ b/autotests/client/test_wayland_outputmanagement.cpp @@ -1,538 +1,537 @@ /* SPDX-FileCopyrightText: 2014 Martin Gräßlin SPDX-FileCopyrightText: 2015 Sebastian Kügler SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ // Qt #include // KWin #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/outputdevice.h" #include "../../src/client/outputconfiguration.h" #include "../../src/client/outputmanagement.h" #include "../../src/client/output.h" #include "../../src/client/registry.h" #include "../../src/server/display.h" #include "../../src/server/shell_interface.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/outputconfiguration_interface.h" #include "../../src/server/outputdevice_interface.h" #include "../../src/server/outputmanagement_interface.h" // Wayland #include using namespace KWayland::Client; using namespace KWayland::Server; class TestWaylandOutputManagement : public QObject { Q_OBJECT public: explicit TestWaylandOutputManagement(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void createConfig(); void testBasicMemoryManagement(); void testMultipleSettings(); void testConfigFailed(); void testApplied(); void testFailed(); void testExampleConfig(); void testScale(); void testRemoval(); private: void createOutputDevices(); void testEnable(); void applyPendingChanges(KWayland::Server::OutputConfigurationInterface *configurationInterface); KWayland::Server::Display *m_display; KWayland::Server::OutputManagementInterface *m_outputManagementInterface; QList m_serverOutputs; KWayland::Client::Registry *m_registry = nullptr; KWayland::Client::OutputDevice *m_outputDevice = nullptr; KWayland::Client::OutputManagement *m_outputManagement = nullptr; KWayland::Client::OutputConfiguration *m_outputConfiguration = nullptr; QList m_clientOutputs; QList m_modes; KWayland::Client::ConnectionThread *m_connection = nullptr; KWayland::Client::EventQueue *m_queue = nullptr; QThread *m_thread; QSignalSpy *m_announcedSpy; QSignalSpy *m_omSpy; - QSignalSpy *m_configSpy; }; static const QString s_socketName = QStringLiteral("kwin-test-wayland-output-0"); TestWaylandOutputManagement::TestWaylandOutputManagement(QObject *parent) : QObject(parent) , m_display(nullptr) , m_outputManagementInterface(nullptr) , m_connection(nullptr) , m_queue(nullptr) , m_thread(nullptr) , m_announcedSpy(nullptr) { qRegisterMetaType(); } void TestWaylandOutputManagement::init() { using namespace KWayland::Server; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); auto shell = m_display->createShell(this); shell->create(); auto comp = m_display->createCompositor(this); comp->create(); auto outputDeviceInterface = m_display->createOutputDevice(this); OutputDeviceInterface::Mode m0; m0.id = 0; m0.size = QSize(800, 600); m0.flags = OutputDeviceInterface::ModeFlags(OutputDeviceInterface::ModeFlag::Preferred); outputDeviceInterface->addMode(m0); OutputDeviceInterface::Mode m1; m1.id = 1; m1.size = QSize(1024, 768); outputDeviceInterface->addMode(m1); OutputDeviceInterface::Mode m2; m2.id = 2; m2.size = QSize(1280, 1024); m2.refreshRate = 90000; outputDeviceInterface->addMode(m2); OutputDeviceInterface::Mode m3; m3.id = 3; m3.size = QSize(1920, 1080); m3.flags = OutputDeviceInterface::ModeFlags(); m3.refreshRate = 100000; outputDeviceInterface->addMode(m3); m_modes << m0 << m1 << m2 << m3; outputDeviceInterface->setCurrentMode(1); outputDeviceInterface->setGlobalPosition(QPoint(0, 1920)); outputDeviceInterface->create(); m_serverOutputs << outputDeviceInterface; m_outputManagementInterface = m_display->createOutputManagement(this); m_outputManagementInterface->create(); QVERIFY(m_outputManagementInterface->isValid()); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, &KWayland::Client::ConnectionThread::connected); 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 KWayland::Client::EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); m_registry = new Registry(); m_announcedSpy = new QSignalSpy(m_registry, &KWayland::Client::Registry::outputManagementAnnounced); m_omSpy = new QSignalSpy(m_registry, &KWayland::Client::Registry::outputDeviceAnnounced); QVERIFY(m_announcedSpy->isValid()); QVERIFY(m_omSpy->isValid()); m_registry->create(m_connection->display()); QVERIFY(m_registry->isValid()); m_registry->setEventQueue(m_queue); m_registry->setup(); wl_display_flush(m_connection->display()); QVERIFY(m_announcedSpy->wait()); QCOMPARE(m_announcedSpy->count(), 1); m_outputManagement = m_registry->createOutputManagement(m_announcedSpy->first().first().value(), m_announcedSpy->first().last().value()); createOutputDevices(); } void TestWaylandOutputManagement::cleanup() { if (m_outputConfiguration) { delete m_outputConfiguration; m_outputConfiguration = nullptr; } if (m_outputManagement) { delete m_outputManagement; m_outputManagement = nullptr; } if (m_registry) { delete m_registry; m_registry = nullptr; } if (m_queue) { delete m_queue; m_queue = nullptr; } if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } if (m_outputManagementInterface) { delete m_outputManagementInterface; m_outputManagementInterface = nullptr; } delete m_display; m_display = nullptr; m_serverOutputs.clear(); m_clientOutputs.clear(); } void TestWaylandOutputManagement::applyPendingChanges(KWayland::Server::OutputConfigurationInterface *configurationInterface) { auto changes = configurationInterface->changes(); for (auto outputdevice: changes.keys()) { auto c = changes[outputdevice]; if (c->enabledChanged()) { outputdevice->setEnabled(c->enabled()); } if (c->modeChanged()) { outputdevice->setCurrentMode(c->mode()); } if (c->transformChanged()) { outputdevice->setTransform(c->transform()); } if (c->positionChanged()) { outputdevice->setGlobalPosition(c->position()); } if (c->scaleChanged()) { outputdevice->setScaleF(c->scaleF()); } if (c->colorCurvesChanged()) { outputdevice->setColorCurves(c->colorCurves()); } } } void TestWaylandOutputManagement::createOutputDevices() { QCOMPARE(m_omSpy->count(), 1); QCOMPARE(m_registry->interfaces(KWayland::Client::Registry::Interface::OutputDevice).count(), m_serverOutputs.count()); auto output = new KWayland::Client::OutputDevice(); QVERIFY(!output->isValid()); QCOMPARE(output->geometry(), QRect()); QCOMPARE(output->globalPosition(), QPoint()); QCOMPARE(output->manufacturer(), QString()); QCOMPARE(output->model(), QString()); QCOMPARE(output->physicalSize(), QSize()); QCOMPARE(output->pixelSize(), QSize()); QCOMPARE(output->refreshRate(), 0); #if KWAYLANDSERVER_ENABLE_DEPRECATED_SINCE(5, 50) QCOMPARE(output->scale(), 1); #endif QCOMPARE(output->scaleF(), 1.0); QCOMPARE(output->colorCurves().red, QVector()); QCOMPARE(output->colorCurves().green, QVector()); QCOMPARE(output->colorCurves().blue, QVector()); QCOMPARE(output->subPixel(), KWayland::Client::OutputDevice::SubPixel::Unknown); QCOMPARE(output->transform(), KWayland::Client::OutputDevice::Transform::Normal); QCOMPARE(output->enabled(), OutputDevice::Enablement::Enabled); QCOMPARE(output->edid(), QByteArray()); QCOMPARE(output->uuid(), QByteArray()); QSignalSpy outputChanged(output, &KWayland::Client::OutputDevice::changed); QVERIFY(outputChanged.isValid()); output->setup(m_registry->bindOutputDevice(m_omSpy->first().first().value(), m_omSpy->first().last().value())); wl_display_flush(m_connection->display()); QVERIFY(outputChanged.wait()); QCOMPARE(output->globalPosition(), QPoint(0, 1920)); QCOMPARE(output->enabled(), OutputDevice::Enablement::Enabled); m_clientOutputs << output; m_outputDevice = output; QVERIFY(m_outputManagement->isValid()); } void TestWaylandOutputManagement::testBasicMemoryManagement() { createConfig(); QSignalSpy serverApplySpy(m_outputManagementInterface, &OutputManagementInterface::configurationChangeRequested); KWayland::Server::OutputConfigurationInterface *configurationInterface = nullptr; connect(m_outputManagementInterface, &OutputManagementInterface::configurationChangeRequested, [=, &configurationInterface](KWayland::Server::OutputConfigurationInterface *c) { configurationInterface = c; }); m_outputConfiguration->apply(); QVERIFY(serverApplySpy.wait()); QVERIFY(configurationInterface); QSignalSpy interfaceDeletedSpy(configurationInterface, &QObject::destroyed); delete m_outputConfiguration; m_outputConfiguration = nullptr; QVERIFY(interfaceDeletedSpy.wait()); } void TestWaylandOutputManagement::testRemoval() { QSignalSpy outputManagementRemovedSpy(m_registry, &KWayland::Client::Registry::outputManagementRemoved); QVERIFY(outputManagementRemovedSpy.isValid()); delete m_outputManagementInterface; m_outputManagementInterface = nullptr; QVERIFY(outputManagementRemovedSpy.wait(200)); QCOMPARE(outputManagementRemovedSpy.first().first(), m_announcedSpy->first().first()); QVERIFY(!m_registry->hasInterface(KWayland::Client::Registry::Interface::OutputManagement)); QVERIFY(m_registry->interfaces(KWayland::Client::Registry::Interface::OutputManagement).isEmpty()); } void TestWaylandOutputManagement::createConfig() { m_outputConfiguration = m_outputManagement->createConfiguration(); } void TestWaylandOutputManagement::testApplied() { createConfig(); QVERIFY(m_outputConfiguration->isValid()); QSignalSpy appliedSpy(m_outputConfiguration, &KWayland::Client::OutputConfiguration::applied); connect(m_outputManagementInterface, &OutputManagementInterface::configurationChangeRequested, [=](KWayland::Server::OutputConfigurationInterface *configurationInterface) { configurationInterface->setApplied(); }); m_outputConfiguration->apply(); QVERIFY(appliedSpy.wait(200)); QCOMPARE(appliedSpy.count(), 1); } void TestWaylandOutputManagement::testFailed() { createConfig(); QVERIFY(m_outputConfiguration->isValid()); QSignalSpy failedSpy(m_outputConfiguration, &KWayland::Client::OutputConfiguration::failed); connect(m_outputManagementInterface, &OutputManagementInterface::configurationChangeRequested, [=](KWayland::Server::OutputConfigurationInterface *configurationInterface) { configurationInterface->setFailed(); }); m_outputConfiguration->apply(); QVERIFY(failedSpy.wait(200)); QCOMPARE(failedSpy.count(), 1); } void TestWaylandOutputManagement::testEnable() { createConfig(); auto config = m_outputConfiguration; QVERIFY(config->isValid()); KWayland::Client::OutputDevice *output = m_clientOutputs.first(); QCOMPARE(output->enabled(), OutputDevice::Enablement::Enabled); QSignalSpy enabledChanged(output, &KWayland::Client::OutputDevice::enabledChanged); QVERIFY(enabledChanged.isValid()); config->setEnabled(output, OutputDevice::Enablement::Disabled); QVERIFY(!enabledChanged.wait(200)); QCOMPARE(enabledChanged.count(), 0); // Reset config->setEnabled(output, OutputDevice::Enablement::Disabled); config->apply(); } void TestWaylandOutputManagement::testMultipleSettings() { createConfig(); auto config = m_outputConfiguration; QVERIFY(config->isValid()); KWayland::Client::OutputDevice *output = m_clientOutputs.first(); QSignalSpy outputChangedSpy(output, &KWayland::Client::OutputDevice::changed); KWayland::Server::OutputConfigurationInterface *configurationInterface; connect(m_outputManagementInterface, &OutputManagementInterface::configurationChangeRequested, [=, &configurationInterface](KWayland::Server::OutputConfigurationInterface *c) { applyPendingChanges(c); configurationInterface = c; }); QSignalSpy serverApplySpy(m_outputManagementInterface, &OutputManagementInterface::configurationChangeRequested); QVERIFY(serverApplySpy.isValid()); config->setMode(output, m_modes.first().id); config->setTransform(output, OutputDevice::Transform::Rotated90); config->setPosition(output, QPoint(13, 37)); config->setScaleF(output, 2.0); const auto zeroVector = QVector(256, 0); config->setColorCurves(output, zeroVector, zeroVector, zeroVector); config->setEnabled(output, OutputDevice::Enablement::Disabled); config->apply(); QVERIFY(serverApplySpy.wait(200)); QCOMPARE(serverApplySpy.count(), 1); configurationInterface->setApplied(); QSignalSpy configAppliedSpy(config, &OutputConfiguration::applied); QVERIFY(configAppliedSpy.isValid()); QVERIFY(configAppliedSpy.wait(200)); QCOMPARE(configAppliedSpy.count(), 1); QCOMPARE(outputChangedSpy.count(), 6); config->setMode(output, m_modes.at(1).id); config->setTransform(output, OutputDevice::Transform::Normal); config->setPosition(output, QPoint(0, 1920)); config->setScaleF(output, 1.0); const auto oneVector = QVector(256, 1); config->setColorCurves(output, oneVector, oneVector, oneVector); config->setEnabled(output, OutputDevice::Enablement::Enabled); config->apply(); QVERIFY(serverApplySpy.wait(200)); QCOMPARE(serverApplySpy.count(), 2); configurationInterface->setApplied(); QVERIFY(configAppliedSpy.wait(200)); QCOMPARE(configAppliedSpy.count(), 2); QCOMPARE(outputChangedSpy.count(), 12); } void TestWaylandOutputManagement::testConfigFailed() { createConfig(); auto config = m_outputConfiguration; auto s_o = m_serverOutputs.first(); KWayland::Client::OutputDevice *output = m_clientOutputs.first(); QVERIFY(config->isValid()); QVERIFY(s_o->isValid()); QVERIFY(output->isValid()); QSignalSpy serverApplySpy(m_outputManagementInterface, &OutputManagementInterface::configurationChangeRequested); QVERIFY(serverApplySpy.isValid()); QSignalSpy outputChangedSpy(output, &KWayland::Client::OutputDevice::changed); QVERIFY(outputChangedSpy.isValid()); QSignalSpy configAppliedSpy(config, &OutputConfiguration::applied); QVERIFY(configAppliedSpy.isValid()); QSignalSpy configFailedSpy(config, &KWayland::Client::OutputConfiguration::failed); QVERIFY(configFailedSpy.isValid()); config->setMode(output, m_modes.last().id); config->setTransform(output, OutputDevice::Transform::Normal); config->setPosition(output, QPoint(-1, -1)); config->apply(); connect(m_outputManagementInterface, &OutputManagementInterface::configurationChangeRequested, [=](KWayland::Server::OutputConfigurationInterface *c) { c->setFailed(); }); QVERIFY(serverApplySpy.wait(200)); // Artificially make the server fail to apply the settings // Make sure the applied signal never comes, and that failed has been received QVERIFY(!configAppliedSpy.wait(200)); QCOMPARE(configFailedSpy.count(), 1); QCOMPARE(configAppliedSpy.count(), 0); } void TestWaylandOutputManagement::testExampleConfig() { createConfig(); auto config = m_outputConfiguration; KWayland::Client::OutputDevice *output = m_clientOutputs.first(); config->setMode(output, m_clientOutputs.first()->modes().last().id); config->setTransform(output, OutputDevice::Transform::Normal); config->setPosition(output, QPoint(-1, -1)); QSignalSpy configAppliedSpy(config, &OutputConfiguration::applied); connect(m_outputManagementInterface, &OutputManagementInterface::configurationChangeRequested, [=](KWayland::Server::OutputConfigurationInterface *c) { c->setApplied(); }); config->apply(); QVERIFY(configAppliedSpy.isValid()); QVERIFY(configAppliedSpy.wait(200)); } void TestWaylandOutputManagement::testScale() { createConfig(); auto config = m_outputConfiguration; KWayland::Client::OutputDevice *output = m_clientOutputs.first(); config->setScaleF(output, 2.3); config->apply(); QSignalSpy configAppliedSpy(config, &OutputConfiguration::applied); connect(m_outputManagementInterface, &OutputManagementInterface::configurationChangeRequested, [=](KWayland::Server::OutputConfigurationInterface *c) { applyPendingChanges(c); c->setApplied(); }); QVERIFY(configAppliedSpy.isValid()); QVERIFY(configAppliedSpy.wait(200)); #if KWAYLANDSERVER_ENABLE_DEPRECATED_SINCE(5, 50) QCOMPARE(output->scale(), 2); //test backwards compatibility #endif QCOMPARE(wl_fixed_from_double(output->scaleF()), wl_fixed_from_double(2.3)); #if KWAYLANDSERVER_ENABLE_DEPRECATED_SINCE(5, 50) config->setScale(output, 3); config->apply(); QVERIFY(configAppliedSpy.isValid()); QVERIFY(configAppliedSpy.wait(200)); //will be setApplied using the connect above QCOMPARE(output->scale(), 3); QCOMPARE(output->scaleF(), 3.0); //test forward compatibility #endif } QTEST_GUILESS_MAIN(TestWaylandOutputManagement) #include "test_wayland_outputmanagement.moc" diff --git a/autotests/client/test_wayland_shell.cpp b/autotests/client/test_wayland_shell.cpp index c48cbf3..e8b46a5 100644 --- a/autotests/client/test_wayland_shell.cpp +++ b/autotests/client/test_wayland_shell.cpp @@ -1,908 +1,908 @@ /* SPDX-FileCopyrightText: 2014 Martin Gräßlin SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ // Qt #include // KWin #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/pointer.h" #include "../../src/client/seat.h" #include "../../src/client/shell.h" #include "../../src/client/surface.h" #include "../../src/client/registry.h" #include "../../src/server/buffer_interface.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/display.h" #include "../../src/server/seat_interface.h" #include "../../src/server/shell_interface.h" #include "../../src/server/surface_interface.h" // Wayland #include Q_DECLARE_METATYPE(Qt::Edges) class TestWaylandShell : public QObject { Q_OBJECT public: explicit TestWaylandShell(QObject *parent = nullptr); private Q_SLOTS: void initTestCase(); void init(); void cleanup(); void testCreateMultiple(); void testFullscreen(); void testMaximize(); void testToplevel(); void testTransient_data(); void testTransient(); void testTransientPopup(); void testPing(); void testTitle(); void testWindowClass(); void testDestroy(); void testCast(); void testMove(); void testResize_data(); void testResize(); void testDisconnect(); void testWhileDestroying(); void testClientDisconnecting(); private: KWayland::Server::Display *m_display; KWayland::Server::CompositorInterface *m_compositorInterface; KWayland::Server::ShellInterface *m_shellInterface; KWayland::Server::SeatInterface *m_seatInterface; KWayland::Client::ConnectionThread *m_connection; KWayland::Client::Compositor *m_compositor; KWayland::Client::Shell *m_shell; KWayland::Client::Seat *m_seat; KWayland::Client::Pointer *m_pointer; KWayland::Client::EventQueue *m_queue; QThread *m_thread; }; static const QString s_socketName = QStringLiteral("kwin-test-wayland-shell-0"); TestWaylandShell::TestWaylandShell(QObject *parent) : QObject(parent) , m_display(nullptr) , m_compositorInterface(nullptr) , m_shellInterface(nullptr) , m_seatInterface(nullptr) , m_connection(nullptr) , m_compositor(nullptr) , m_shell(nullptr) , m_seat(nullptr) , m_pointer(nullptr) , m_queue(nullptr) , m_thread(nullptr) { } void TestWaylandShell::initTestCase() { qRegisterMetaType(); } void TestWaylandShell::init() { using namespace KWayland::Server; delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); m_compositorInterface = m_display->createCompositor(m_display); QVERIFY(m_compositorInterface); m_compositorInterface->create(); QVERIFY(m_compositorInterface->isValid()); m_shellInterface = m_display->createShell(m_display); QVERIFY(m_shellInterface); m_shellInterface->create(); QVERIFY(m_shellInterface->isValid()); m_seatInterface = m_display->createSeat(m_display); QVERIFY(m_seatInterface); m_seatInterface->setHasPointer(true); m_seatInterface->create(); QVERIFY(m_seatInterface->isValid()); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, SIGNAL(connected())); 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 KWayland::Client::EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); using namespace KWayland::Client; KWayland::Client::Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); QSignalSpy compositorSpy(®istry, SIGNAL(compositorAnnounced(quint32,quint32))); QSignalSpy shellSpy(®istry, SIGNAL(shellAnnounced(quint32,quint32))); QSignalSpy seatAnnouncedSpy(®istry, &Registry::seatAnnounced); QVERIFY(seatAnnouncedSpy.isValid()); QVERIFY(!registry.eventQueue()); registry.setEventQueue(m_queue); QCOMPARE(registry.eventQueue(), m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(interfacesAnnouncedSpy.wait()); m_compositor = new KWayland::Client::Compositor(this); m_compositor->setup(registry.bindCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value())); QVERIFY(m_compositor->isValid()); if (shellSpy.isEmpty()) { QVERIFY(shellSpy.wait()); } m_shell = registry.createShell(shellSpy.first().first().value(), shellSpy.first().last().value(), this); QVERIFY(m_shell->isValid()); QVERIFY(!seatAnnouncedSpy.isEmpty()); m_seat = registry.createSeat(seatAnnouncedSpy.first().first().value(), seatAnnouncedSpy.first().last().value(), this); QVERIFY(seatAnnouncedSpy.isValid()); QSignalSpy hasPointerSpy(m_seat, &Seat::hasPointerChanged); QVERIFY(hasPointerSpy.isValid()); QVERIFY(hasPointerSpy.wait()); QVERIFY(hasPointerSpy.first().first().toBool()); m_pointer = m_seat->createPointer(m_seat); QVERIFY(m_pointer->isValid()); } void TestWaylandShell::cleanup() { if (m_pointer) { delete m_pointer; m_pointer = nullptr; } if (m_seat) { delete m_seat; m_seat = nullptr; } if (m_shell) { delete m_shell; m_shell = nullptr; } if (m_compositor) { delete m_compositor; m_compositor = nullptr; } if (m_queue) { delete m_queue; m_queue = nullptr; } if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } delete m_seatInterface; m_seatInterface = nullptr; delete m_shellInterface; m_shellInterface = nullptr; delete m_compositorInterface; m_compositorInterface = nullptr; delete m_display; m_display = nullptr; } void TestWaylandShell::testCreateMultiple() { using namespace KWayland::Server; using namespace KWayland::Client; QScopedPointer s1(m_compositor->createSurface()); QScopedPointer s2(m_compositor->createSurface()); QVERIFY(!s1.isNull()); QVERIFY(s1->isValid()); QVERIFY(!s2.isNull()); QVERIFY(s2->isValid()); QSignalSpy serverSurfaceSpy(m_shellInterface, SIGNAL(surfaceCreated(KWayland::Server::ShellSurfaceInterface*))); QVERIFY(serverSurfaceSpy.isValid()); QScopedPointer surface1(m_shell->createSurface(s1.data())); QVERIFY(!surface1.isNull()); QVERIFY(surface1->isValid()); QVERIFY(!ShellSurface::get(nullptr)); QCOMPARE(ShellSurface::get(*(surface1.data())), surface1.data()); QVERIFY(serverSurfaceSpy.wait()); QCOMPARE(serverSurfaceSpy.count(), 1); QScopedPointer surface2(m_shell->createSurface(s2.data())); QVERIFY(!surface2.isNull()); QVERIFY(surface2->isValid()); QCOMPARE(ShellSurface::get(*(surface2.data())), surface2.data()); QVERIFY(serverSurfaceSpy.wait()); QCOMPARE(serverSurfaceSpy.count(), 2); // try creating for one which already exist should not be possible QScopedPointer surface3(m_shell->createSurface(s2.data())); QVERIFY(!surface3.isNull()); QVERIFY(surface3->isValid()); QCOMPARE(ShellSurface::get(*(surface3.data())), surface3.data()); QVERIFY(!serverSurfaceSpy.wait(100)); QCOMPARE(serverSurfaceSpy.count(), 2); } void TestWaylandShell::testFullscreen() { using namespace KWayland::Server; QScopedPointer s(m_compositor->createSurface()); QVERIFY(!s.isNull()); QVERIFY(s->isValid()); KWayland::Client::ShellSurface *surface = m_shell->createSurface(s.data(), m_shell); QSignalSpy sizeSpy(surface, SIGNAL(sizeChanged(QSize))); QVERIFY(sizeSpy.isValid()); QCOMPARE(surface->size(), QSize()); QSignalSpy serverSurfaceSpy(m_shellInterface, SIGNAL(surfaceCreated(KWayland::Server::ShellSurfaceInterface*))); QVERIFY(serverSurfaceSpy.isValid()); QVERIFY(serverSurfaceSpy.wait()); ShellSurfaceInterface *serverSurface = serverSurfaceSpy.first().first().value(); QVERIFY(serverSurface); QVERIFY(serverSurface->parentResource()); QCOMPARE(serverSurface->shell(), m_shellInterface); QSignalSpy fullscreenSpy(serverSurface, SIGNAL(fullscreenChanged(bool))); QVERIFY(fullscreenSpy.isValid()); QVERIFY(!serverSurface->isFullscreen()); surface->setFullscreen(); QVERIFY(fullscreenSpy.wait()); QCOMPARE(fullscreenSpy.count(), 1); QVERIFY(fullscreenSpy.first().first().toBool()); QVERIFY(serverSurface->isFullscreen()); serverSurface->requestSize(QSize(1024, 768)); QVERIFY(sizeSpy.wait()); QCOMPARE(sizeSpy.count(), 1); QCOMPARE(sizeSpy.first().first().toSize(), QSize(1024, 768)); QCOMPARE(surface->size(), QSize(1024, 768)); // set back to toplevel fullscreenSpy.clear(); wl_shell_surface_set_toplevel(*surface); QVERIFY(fullscreenSpy.wait()); QCOMPARE(fullscreenSpy.count(), 1); QVERIFY(!fullscreenSpy.first().first().toBool()); QVERIFY(!serverSurface->isFullscreen()); } void TestWaylandShell::testMaximize() { using namespace KWayland::Server; QScopedPointer s(m_compositor->createSurface()); QVERIFY(!s.isNull()); QVERIFY(s->isValid()); KWayland::Client::ShellSurface *surface = m_shell->createSurface(s.data(), m_shell); QSignalSpy sizeSpy(surface, SIGNAL(sizeChanged(QSize))); QVERIFY(sizeSpy.isValid()); QCOMPARE(surface->size(), QSize()); QSignalSpy serverSurfaceSpy(m_shellInterface, SIGNAL(surfaceCreated(KWayland::Server::ShellSurfaceInterface*))); QVERIFY(serverSurfaceSpy.isValid()); QVERIFY(serverSurfaceSpy.wait()); ShellSurfaceInterface *serverSurface = serverSurfaceSpy.first().first().value(); QVERIFY(serverSurface); QVERIFY(serverSurface->parentResource()); QSignalSpy maximizedSpy(serverSurface, SIGNAL(maximizedChanged(bool))); QVERIFY(maximizedSpy.isValid()); QVERIFY(!serverSurface->isMaximized()); surface->setMaximized(); QVERIFY(maximizedSpy.wait()); QCOMPARE(maximizedSpy.count(), 1); QVERIFY(maximizedSpy.first().first().toBool()); QVERIFY(serverSurface->isMaximized()); serverSurface->requestSize(QSize(1024, 768)); QVERIFY(sizeSpy.wait()); QCOMPARE(sizeSpy.count(), 1); QCOMPARE(sizeSpy.first().first().toSize(), QSize(1024, 768)); QCOMPARE(surface->size(), QSize(1024, 768)); // set back to toplevel maximizedSpy.clear(); wl_shell_surface_set_toplevel(*surface); QVERIFY(maximizedSpy.wait()); QCOMPARE(maximizedSpy.count(), 1); QVERIFY(!maximizedSpy.first().first().toBool()); QVERIFY(!serverSurface->isMaximized()); } void TestWaylandShell::testToplevel() { using namespace KWayland::Server; QScopedPointer s(m_compositor->createSurface()); QVERIFY(!s.isNull()); QVERIFY(s->isValid()); KWayland::Client::ShellSurface *surface = m_shell->createSurface(s.data(), m_shell); QSignalSpy sizeSpy(surface, SIGNAL(sizeChanged(QSize))); QVERIFY(sizeSpy.isValid()); QCOMPARE(surface->size(), QSize()); QSignalSpy serverSurfaceSpy(m_shellInterface, SIGNAL(surfaceCreated(KWayland::Server::ShellSurfaceInterface*))); QVERIFY(serverSurfaceSpy.isValid()); QVERIFY(serverSurfaceSpy.wait()); ShellSurfaceInterface *serverSurface = serverSurfaceSpy.first().first().value(); QVERIFY(serverSurface); QVERIFY(serverSurface->parentResource()); QSignalSpy toplevelSpy(serverSurface, SIGNAL(toplevelChanged(bool))); QVERIFY(toplevelSpy.isValid()); surface->setFullscreen(); QVERIFY(toplevelSpy.wait()); QCOMPARE(toplevelSpy.count(), 1); QVERIFY(!toplevelSpy.first().first().toBool()); toplevelSpy.clear(); surface->setToplevel(); QVERIFY(toplevelSpy.wait()); QCOMPARE(toplevelSpy.count(), 1); QVERIFY(toplevelSpy.first().first().toBool()); serverSurface->requestSize(QSize(1024, 768)); QVERIFY(sizeSpy.wait()); QCOMPARE(sizeSpy.count(), 1); QCOMPARE(sizeSpy.first().first().toSize(), QSize(1024, 768)); QCOMPARE(surface->size(), QSize(1024, 768)); // set back to fullscreen toplevelSpy.clear(); surface->setFullscreen(); QVERIFY(toplevelSpy.wait()); QCOMPARE(toplevelSpy.count(), 1); QVERIFY(!toplevelSpy.first().first().toBool()); } void TestWaylandShell::testTransient_data() { QTest::addColumn("keyboardFocus"); QTest::newRow("focus") << true; QTest::newRow("no focus") << false; } void TestWaylandShell::testTransient() { using namespace KWayland::Server; using namespace KWayland::Client; QScopedPointer s(m_compositor->createSurface()); QVERIFY(!s.isNull()); QVERIFY(s->isValid()); ShellSurface *surface = m_shell->createSurface(s.data(), m_shell); QSignalSpy serverSurfaceSpy(m_shellInterface, &ShellInterface::surfaceCreated); QVERIFY(serverSurfaceSpy.isValid()); QVERIFY(serverSurfaceSpy.wait()); ShellSurfaceInterface *serverSurface = serverSurfaceSpy.first().first().value(); QVERIFY(serverSurface); QSignalSpy acceptsKeyboardFocusChangedSpy(serverSurface, &ShellSurfaceInterface::acceptsKeyboardFocusChanged); QVERIFY(acceptsKeyboardFocusChangedSpy.isValid()); QCOMPARE(serverSurface->isToplevel(), true); QCOMPARE(serverSurface->isPopup(), false); QCOMPARE(serverSurface->isTransient(), false); QCOMPARE(serverSurface->transientFor(), QPointer()); QCOMPARE(serverSurface->transientOffset(), QPoint()); QVERIFY(serverSurface->acceptsKeyboardFocus()); QVERIFY(acceptsKeyboardFocusChangedSpy.isEmpty()); QSignalSpy transientSpy(serverSurface, &ShellSurfaceInterface::transientChanged); QVERIFY(transientSpy.isValid()); QSignalSpy transientOffsetSpy(serverSurface, &ShellSurfaceInterface::transientOffsetChanged); QVERIFY(transientOffsetSpy.isValid()); QSignalSpy transientForChangedSpy(serverSurface, &ShellSurfaceInterface::transientForChanged); QVERIFY(transientForChangedSpy.isValid()); QScopedPointer s2(m_compositor->createSurface()); m_shell->createSurface(s2.data(), m_shell); serverSurfaceSpy.clear(); QVERIFY(serverSurfaceSpy.wait()); ShellSurfaceInterface *serverSurface2 = serverSurfaceSpy.first().first().value(); QVERIFY(serverSurface2 != serverSurface); QVERIFY(serverSurface2); QVERIFY(serverSurface2->acceptsKeyboardFocus()); QFETCH(bool, keyboardFocus); surface->setTransient(s2.data(), QPoint(10, 20), keyboardFocus ? ShellSurface::TransientFlag::Default : ShellSurface::TransientFlag::NoFocus); QVERIFY(transientSpy.wait()); QCOMPARE(transientSpy.count(), 1); QCOMPARE(transientSpy.first().first().toBool(), true); QCOMPARE(transientOffsetSpy.count(), 1); QCOMPARE(transientOffsetSpy.first().first().toPoint(), QPoint(10, 20)); QCOMPARE(transientForChangedSpy.count(), 1); QCOMPARE(serverSurface->isToplevel(), true); QCOMPARE(serverSurface->isPopup(), false); QCOMPARE(serverSurface->isTransient(), true); QCOMPARE(serverSurface->transientFor(), QPointer(serverSurface2->surface())); QCOMPARE(serverSurface->transientOffset(), QPoint(10, 20)); QCOMPARE(serverSurface->acceptsKeyboardFocus(), keyboardFocus); QCOMPARE(acceptsKeyboardFocusChangedSpy.isEmpty(), keyboardFocus); QCOMPARE(acceptsKeyboardFocusChangedSpy.count(), keyboardFocus ? 0 : 1); QCOMPARE(serverSurface2->isToplevel(), true); QCOMPARE(serverSurface2->isPopup(), false); QCOMPARE(serverSurface2->isTransient(), false); QCOMPARE(serverSurface2->transientFor(), QPointer()); QCOMPARE(serverSurface2->transientOffset(), QPoint()); QVERIFY(serverSurface2->acceptsKeyboardFocus()); } void TestWaylandShell::testTransientPopup() { using namespace KWayland::Server; using namespace KWayland::Client; QScopedPointer s(m_compositor->createSurface()); QVERIFY(!s.isNull()); QVERIFY(s->isValid()); ShellSurface *surface = m_shell->createSurface(s.data(), m_shell); QSignalSpy serverSurfaceSpy(m_shellInterface, &ShellInterface::surfaceCreated); QVERIFY(serverSurfaceSpy.isValid()); QVERIFY(serverSurfaceSpy.wait()); ShellSurfaceInterface *serverSurface = serverSurfaceSpy.first().first().value(); QVERIFY(serverSurface); QCOMPARE(serverSurface->isToplevel(), true); QCOMPARE(serverSurface->isPopup(), false); QCOMPARE(serverSurface->isTransient(), false); QCOMPARE(serverSurface->transientFor(), QPointer()); QCOMPARE(serverSurface->transientOffset(), QPoint()); QVERIFY(serverSurface->acceptsKeyboardFocus()); QSignalSpy transientSpy(serverSurface, &ShellSurfaceInterface::transientChanged); QVERIFY(transientSpy.isValid()); QSignalSpy transientOffsetSpy(serverSurface, &ShellSurfaceInterface::transientOffsetChanged); QVERIFY(transientOffsetSpy.isValid()); QSignalSpy transientForChangedSpy(serverSurface, &ShellSurfaceInterface::transientForChanged); QVERIFY(transientForChangedSpy.isValid()); QScopedPointer s2(m_compositor->createSurface()); m_shell->createSurface(s2.data(), m_shell); serverSurfaceSpy.clear(); QVERIFY(serverSurfaceSpy.wait()); ShellSurfaceInterface *serverSurface2 = serverSurfaceSpy.first().first().value(); QVERIFY(serverSurface2 != serverSurface); QVERIFY(serverSurface2); QVERIFY(serverSurface2->acceptsKeyboardFocus()); // TODO: proper serial checking surface->setTransientPopup(s2.data(), m_seat, 1, QPoint(10, 20)); QVERIFY(transientSpy.wait()); QCOMPARE(transientSpy.count(), 1); QCOMPARE(transientSpy.first().first().toBool(), true); QCOMPARE(transientOffsetSpy.count(), 1); QCOMPARE(transientOffsetSpy.first().first().toPoint(), QPoint(10, 20)); QCOMPARE(transientForChangedSpy.count(), 1); QCOMPARE(serverSurface->isToplevel(), false); QCOMPARE(serverSurface->isPopup(), true); QCOMPARE(serverSurface->isTransient(), true); QCOMPARE(serverSurface->transientFor(), QPointer(serverSurface2->surface())); QCOMPARE(serverSurface->transientOffset(), QPoint(10, 20)); // TODO: honor the flag QCOMPARE(serverSurface->acceptsKeyboardFocus(), false); QCOMPARE(serverSurface2->isToplevel(), true); QCOMPARE(serverSurface2->isPopup(), false); QCOMPARE(serverSurface2->isTransient(), false); QCOMPARE(serverSurface2->transientFor(), QPointer()); QCOMPARE(serverSurface2->transientOffset(), QPoint()); // send popup done QSignalSpy popupDoneSpy(surface, &ShellSurface::popupDone); QVERIFY(popupDoneSpy.isValid()); serverSurface->popupDone(); QVERIFY(popupDoneSpy.wait()); } void TestWaylandShell::testPing() { using namespace KWayland::Server; QScopedPointer s(m_compositor->createSurface()); QVERIFY(!s.isNull()); QVERIFY(s->isValid()); KWayland::Client::ShellSurface *surface = m_shell->createSurface(s.data(), m_shell); QSignalSpy pingSpy(surface, SIGNAL(pinged())); QSignalSpy serverSurfaceSpy(m_shellInterface, SIGNAL(surfaceCreated(KWayland::Server::ShellSurfaceInterface*))); QVERIFY(serverSurfaceSpy.isValid()); QVERIFY(serverSurfaceSpy.wait()); ShellSurfaceInterface *serverSurface = serverSurfaceSpy.first().first().value(); QVERIFY(serverSurface); QVERIFY(!serverSurface->isPinged()); QSignalSpy pingTimeoutSpy(serverSurface, SIGNAL(pingTimeout())); QVERIFY(pingTimeoutSpy.isValid()); QSignalSpy pongSpy(serverSurface, SIGNAL(pongReceived())); QVERIFY(pongSpy.isValid()); serverSurface->ping(); QVERIFY(serverSurface->isPinged()); QVERIFY(pingSpy.wait()); wl_display_flush(m_connection->display()); if (pongSpy.isEmpty()) { QVERIFY(pongSpy.wait()); } QVERIFY(!pongSpy.isEmpty()); QVERIFY(pingTimeoutSpy.isEmpty()); // evil trick - timeout of zero will make it not get the pong serverSurface->setPingTimeout(0); pongSpy.clear(); pingTimeoutSpy.clear(); serverSurface->ping(); QTest::qWait(100); if (pingTimeoutSpy.isEmpty()) { QVERIFY(pingTimeoutSpy.wait()); } QCOMPARE(pingTimeoutSpy.count(), 1); QVERIFY(pongSpy.isEmpty()); } void TestWaylandShell::testTitle() { using namespace KWayland::Server; QScopedPointer s(m_compositor->createSurface()); QVERIFY(!s.isNull()); QVERIFY(s->isValid()); KWayland::Client::ShellSurface *surface = m_shell->createSurface(s.data(), m_shell); QSignalSpy serverSurfaceSpy(m_shellInterface, SIGNAL(surfaceCreated(KWayland::Server::ShellSurfaceInterface*))); QVERIFY(serverSurfaceSpy.isValid()); QVERIFY(serverSurfaceSpy.wait()); ShellSurfaceInterface *serverSurface = serverSurfaceSpy.first().first().value(); QVERIFY(serverSurface); QSignalSpy titleSpy(serverSurface, SIGNAL(titleChanged(QString))); QVERIFY(titleSpy.isValid()); QString testTitle = QStringLiteral("fooBar"); QVERIFY(serverSurface->title().isNull()); surface->setTitle(testTitle); QVERIFY(titleSpy.wait()); QCOMPARE(serverSurface->title(), testTitle); QCOMPARE(titleSpy.first().first().toString(), testTitle); } void TestWaylandShell::testWindowClass() { using namespace KWayland::Server; QScopedPointer s(m_compositor->createSurface()); QVERIFY(!s.isNull()); QVERIFY(s->isValid()); KWayland::Client::ShellSurface *surface = m_shell->createSurface(s.data(), m_shell); QSignalSpy serverSurfaceSpy(m_shellInterface, SIGNAL(surfaceCreated(KWayland::Server::ShellSurfaceInterface*))); QVERIFY(serverSurfaceSpy.isValid()); QVERIFY(serverSurfaceSpy.wait()); ShellSurfaceInterface *serverSurface = serverSurfaceSpy.first().first().value(); QVERIFY(serverSurface); QSignalSpy windowClassSpy(serverSurface, SIGNAL(windowClassChanged(QByteArray))); QVERIFY(windowClassSpy.isValid()); QByteArray testClass = QByteArrayLiteral("fooBar"); QVERIFY(serverSurface->windowClass().isNull()); surface->setWindowClass(testClass); QVERIFY(windowClassSpy.wait()); QCOMPARE(serverSurface->windowClass(), testClass); QCOMPARE(windowClassSpy.first().first().toByteArray(), testClass); // try setting it to same should not trigger the signal surface->setWindowClass(testClass); QVERIFY(!windowClassSpy.wait(100)); } void TestWaylandShell::testDestroy() { using namespace KWayland::Client; QScopedPointer s(m_compositor->createSurface()); QVERIFY(!s.isNull()); QVERIFY(s->isValid()); ShellSurface *surface = m_shell->createSurface(s.data(), m_shell); QVERIFY(surface->isValid()); connect(m_connection, &ConnectionThread::connectionDied, m_shell, &Shell::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_pointer, &Pointer::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_seat, &Seat::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_compositor, &Compositor::destroy); connect(m_connection, &ConnectionThread::connectionDied, s.data(), &Surface::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_queue, &EventQueue::destroy); QSignalSpy connectionDiedSpy(m_connection, SIGNAL(connectionDied())); QVERIFY(connectionDiedSpy.isValid()); delete m_display; m_display = nullptr; m_compositorInterface = nullptr; m_shellInterface = nullptr; m_seatInterface = nullptr; QVERIFY(connectionDiedSpy.wait()); QVERIFY(!m_shell->isValid()); QVERIFY(!surface->isValid()); m_shell->destroy(); surface->destroy(); } void TestWaylandShell::testCast() { using namespace KWayland::Client; Registry registry; QSignalSpy shellSpy(®istry, SIGNAL(shellAnnounced(quint32,quint32))); registry.setEventQueue(m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(shellSpy.wait()); Shell s; auto wlShell = registry.bindShell(shellSpy.first().first().value(), shellSpy.first().last().value()); m_queue->addProxy(wlShell); QVERIFY(wlShell); QVERIFY(!s.isValid()); s.setup(wlShell); QVERIFY(s.isValid()); QCOMPARE((wl_shell*)s, wlShell); const Shell &s2(s); QCOMPARE((wl_shell*)s2, wlShell); } void TestWaylandShell::testMove() { using namespace KWayland::Client; using namespace KWayland::Server; QScopedPointer s(m_compositor->createSurface()); QVERIFY(!s.isNull()); QVERIFY(s->isValid()); ShellSurface *surface = m_shell->createSurface(s.data(), m_shell); QSignalSpy serverSurfaceSpy(m_shellInterface, &ShellInterface::surfaceCreated); QVERIFY(serverSurfaceSpy.isValid()); QVERIFY(serverSurfaceSpy.wait()); ShellSurfaceInterface *serverSurface = serverSurfaceSpy.first().first().value(); QVERIFY(serverSurface); QSignalSpy moveRequestedSpy(serverSurface, &ShellSurfaceInterface::moveRequested); QVERIFY(moveRequestedSpy.isValid()); QSignalSpy pointerButtonChangedSpy(m_pointer, &Pointer::buttonStateChanged); QVERIFY(pointerButtonChangedSpy.isValid()); m_seatInterface->setFocusedPointerSurface(serverSurface->surface()); m_seatInterface->pointerButtonPressed(Qt::LeftButton); QVERIFY(pointerButtonChangedSpy.wait()); surface->requestMove(m_seat, pointerButtonChangedSpy.first().first().value()); QVERIFY(moveRequestedSpy.wait()); QCOMPARE(moveRequestedSpy.count(), 1); QCOMPARE(moveRequestedSpy.first().at(0).value(), m_seatInterface); QCOMPARE(moveRequestedSpy.first().at(1).value(), m_seatInterface->pointerButtonSerial(Qt::LeftButton)); } void TestWaylandShell::testResize_data() { QTest::addColumn("resizeEdge"); QTest::addColumn("expectedEdge"); QTest::newRow("None") << Qt::Edges() << Qt::Edges(); QTest::newRow("Top") << Qt::Edges(Qt::TopEdge) << Qt::Edges(Qt::TopEdge); QTest::newRow("Bottom") << Qt::Edges(Qt::BottomEdge) << Qt::Edges(Qt::BottomEdge); QTest::newRow("Left") << Qt::Edges(Qt::LeftEdge) << Qt::Edges(Qt::LeftEdge); QTest::newRow("Right") << Qt::Edges(Qt::RightEdge) << Qt::Edges(Qt::RightEdge); QTest::newRow("Top Left") << Qt::Edges(Qt::TopEdge | Qt::LeftEdge) << Qt::Edges(Qt::TopEdge | Qt::LeftEdge); QTest::newRow("Top Right") << Qt::Edges(Qt::TopEdge | Qt::RightEdge) << Qt::Edges(Qt::TopEdge | Qt::RightEdge); QTest::newRow("Bottom Left") << Qt::Edges(Qt::BottomEdge | Qt::LeftEdge) << Qt::Edges(Qt::BottomEdge | Qt::LeftEdge); QTest::newRow("Bottom Right") << Qt::Edges(Qt::BottomEdge | Qt::RightEdge) << Qt::Edges(Qt::BottomEdge | Qt::RightEdge); // invalid combinations QTest::newRow("Top Bottom") << Qt::Edges(Qt::TopEdge | Qt::BottomEdge) << Qt::Edges(); QTest::newRow("Left Right") << Qt::Edges(Qt::RightEdge | Qt::LeftEdge) << Qt::Edges(); QTest::newRow("Top Bottom Right") << Qt::Edges(Qt::TopEdge | Qt::BottomEdge | Qt::RightEdge) << Qt::Edges(); QTest::newRow("Top Bottom Left") << Qt::Edges(Qt::TopEdge | Qt::BottomEdge | Qt::LeftEdge) << Qt::Edges(); QTest::newRow("Left Right Top") << Qt::Edges(Qt::RightEdge | Qt::LeftEdge | Qt::TopEdge) << Qt::Edges(); QTest::newRow("Left Right Bottom") << Qt::Edges(Qt::RightEdge | Qt::LeftEdge | Qt::BottomEdge) << Qt::Edges(); QTest::newRow("All") << Qt::Edges(Qt::RightEdge | Qt::LeftEdge | Qt::BottomEdge | Qt::TopEdge) << Qt::Edges(); } void TestWaylandShell::testResize() { using namespace KWayland::Client; using namespace KWayland::Server; QScopedPointer s(m_compositor->createSurface()); QVERIFY(!s.isNull()); QVERIFY(s->isValid()); ShellSurface *surface = m_shell->createSurface(s.data(), m_shell); QSignalSpy serverSurfaceSpy(m_shellInterface, &ShellInterface::surfaceCreated); QVERIFY(serverSurfaceSpy.isValid()); QVERIFY(serverSurfaceSpy.wait()); ShellSurfaceInterface *serverSurface = serverSurfaceSpy.first().first().value(); QVERIFY(serverSurface); QSignalSpy resizeRequestedSpy(serverSurface, &ShellSurfaceInterface::resizeRequested); QVERIFY(resizeRequestedSpy.isValid()); QSignalSpy pointerButtonChangedSpy(m_pointer, &Pointer::buttonStateChanged); QVERIFY(pointerButtonChangedSpy.isValid()); m_seatInterface->setFocusedPointerSurface(serverSurface->surface()); m_seatInterface->pointerButtonPressed(Qt::LeftButton); QVERIFY(pointerButtonChangedSpy.wait()); QFETCH(Qt::Edges, resizeEdge); surface->requestResize(m_seat, pointerButtonChangedSpy.first().first().value(), resizeEdge); QVERIFY(resizeRequestedSpy.wait()); QCOMPARE(resizeRequestedSpy.count(), 1); QCOMPARE(resizeRequestedSpy.first().at(0).value(), m_seatInterface); QCOMPARE(resizeRequestedSpy.first().at(1).value(), m_seatInterface->pointerButtonSerial(Qt::LeftButton)); QTEST(resizeRequestedSpy.first().at(2).value(), "expectedEdge"); } void TestWaylandShell::testDisconnect() { // this test verifies that the server side correctly tears down the resources when the client disconnects using namespace KWayland::Client; using namespace KWayland::Server; QScopedPointer s(m_compositor->createSurface()); QVERIFY(!s.isNull()); QVERIFY(s->isValid()); QScopedPointer surface(m_shell->createSurface(s.data(), m_shell)); QSignalSpy serverSurfaceSpy(m_shellInterface, &ShellInterface::surfaceCreated); QVERIFY(serverSurfaceSpy.isValid()); QVERIFY(serverSurfaceSpy.wait()); ShellSurfaceInterface *serverSurface = serverSurfaceSpy.first().first().value(); QVERIFY(serverSurface); // destroy client QSignalSpy clientDisconnectedSpy(serverSurface->client(), &ClientConnection::disconnected); QVERIFY(clientDisconnectedSpy.isValid()); QSignalSpy shellSurfaceDestroyedSpy(serverSurface, &QObject::destroyed); QVERIFY(shellSurfaceDestroyedSpy.isValid()); if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } QVERIFY(clientDisconnectedSpy.wait()); QCOMPARE(clientDisconnectedSpy.count(), 1); QCOMPARE(shellSurfaceDestroyedSpy.count(), 0); // it's already unbound, but the shell surface is not yet destroyed QVERIFY(!serverSurface->resource()); // ensure we don't crash when accessing it in this state serverSurface->ping(); serverSurface->requestSize(QSize(1, 2)); QVERIFY(shellSurfaceDestroyedSpy.wait()); QCOMPARE(shellSurfaceDestroyedSpy.count(), 1); s->destroy(); surface->destroy(); m_shell->destroy(); m_compositor->destroy(); m_pointer->destroy(); m_seat->destroy(); m_queue->destroy(); } void TestWaylandShell::testWhileDestroying() { // this test tries to hit a condition that a Surface gets created with an ID which was already // used for a previous Surface. For each Surface we try to create a ShellSurface. // Even if there was a Surface in the past with the same ID, it should create the ShellSurface using namespace KWayland::Client; using namespace KWayland::Server; QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QVERIFY(surfaceCreatedSpy.isValid()); QScopedPointer s(m_compositor->createSurface()); QVERIFY(surfaceCreatedSpy.wait()); auto serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); // create ShellSurface QSignalSpy shellSurfaceCreatedSpy(m_shellInterface, &ShellInterface::surfaceCreated); QVERIFY(shellSurfaceCreatedSpy.isValid()); QScopedPointer ps(m_shell->createSurface(s.data())); QVERIFY(shellSurfaceCreatedSpy.wait()); // now try to create more surfaces QSignalSpy clientErrorSpy(m_connection, &ConnectionThread::errorOccurred); QVERIFY(clientErrorSpy.isValid()); for (int i = 0; i < 100; i++) { s.reset(); ps.reset(); s.reset(m_compositor->createSurface()); ps.reset(m_shell->createSurface(s.data())); QVERIFY(surfaceCreatedSpy.wait()); } QVERIFY(clientErrorSpy.isEmpty()); QVERIFY(!clientErrorSpy.wait(100)); QVERIFY(clientErrorSpy.isEmpty()); } void TestWaylandShell::testClientDisconnecting() { // this test tries to request a new surface size while the client is actually already destroyed // see BUG: 370232 using namespace KWayland::Client; using namespace KWayland::Server; // create ShellSurface QScopedPointer s(m_compositor->createSurface()); QSignalSpy shellSurfaceCreatedSpy(m_shellInterface, &ShellInterface::surfaceCreated); QVERIFY(shellSurfaceCreatedSpy.isValid()); QScopedPointer ps(m_shell->createSurface(s.data())); QVERIFY(shellSurfaceCreatedSpy.wait()); auto serverShellSurface = shellSurfaceCreatedSpy.first().first().value(); QVERIFY(serverShellSurface); QSignalSpy shellSurfaceUnboundSpy(serverShellSurface, &Resource::unbound); QVERIFY(shellSurfaceUnboundSpy.isValid()); QScopedPointer s2(m_compositor->createSurface()); QScopedPointer ps2(m_shell->createSurface(s2.data())); QVERIFY(shellSurfaceCreatedSpy.wait()); auto serverShellSurface2 = shellSurfaceCreatedSpy.last().first().value(); QVERIFY(serverShellSurface2); connect(serverShellSurface, &Resource::unbound, this, - [serverShellSurface, serverShellSurface2] { + [serverShellSurface2] { serverShellSurface2->requestSize(QSize(100, 200)); } ); m_connection->deleteLater(); m_connection = nullptr; m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; QVERIFY(shellSurfaceUnboundSpy.wait()); ps->destroy(); s->destroy(); ps2->destroy(); s2->destroy(); m_pointer->destroy(); m_seat->destroy(); m_shell->destroy(); m_compositor->destroy(); m_queue->destroy(); } QTEST_GUILESS_MAIN(TestWaylandShell) #include "test_wayland_shell.moc" diff --git a/src/client/appmenu.cpp b/src/client/appmenu.cpp index fb0f5b9..3d16d8d 100644 --- a/src/client/appmenu.cpp +++ b/src/client/appmenu.cpp @@ -1,170 +1,160 @@ /* SPDX-FileCopyrightText: 2017 David Edmundson SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ #include "appmenu.h" #include "event_queue.h" #include "surface.h" #include "wayland_pointer_p.h" #include namespace KWayland { namespace Client { class AppMenuManager::Private { public: Private() = default; void setup(org_kde_kwin_appmenu_manager *arg); WaylandPointer appmenumanager; EventQueue *queue = nullptr; }; AppMenuManager::AppMenuManager(QObject *parent) : QObject(parent) , d(new Private) { } void AppMenuManager::Private::setup(org_kde_kwin_appmenu_manager *arg) { Q_ASSERT(arg); Q_ASSERT(!appmenumanager); appmenumanager.setup(arg); } AppMenuManager::~AppMenuManager() { release(); } void AppMenuManager::setup(org_kde_kwin_appmenu_manager *appmenumanager) { d->setup(appmenumanager); } void AppMenuManager::release() { d->appmenumanager.release(); } void AppMenuManager::destroy() { d->appmenumanager.destroy(); } AppMenuManager::operator org_kde_kwin_appmenu_manager*() { return d->appmenumanager; } AppMenuManager::operator org_kde_kwin_appmenu_manager*() const { return d->appmenumanager; } bool AppMenuManager::isValid() const { return d->appmenumanager.isValid(); } void AppMenuManager::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *AppMenuManager::eventQueue() { return d->queue; } AppMenu *AppMenuManager::create(Surface *surface, QObject *parent) { Q_ASSERT(isValid()); auto p = new AppMenu(parent); auto w = org_kde_kwin_appmenu_manager_create(d->appmenumanager, *surface); if (d->queue) { d->queue->addProxy(w); } p->setup(w); return p; } class AppMenu::Private { public: - Private(AppMenu *q); - void setup(org_kde_kwin_appmenu *arg); WaylandPointer appmenu; - -private: - AppMenu *q; }; -AppMenu::Private::Private(AppMenu *q) - : q(q) -{ -} - AppMenu::AppMenu(QObject *parent) : QObject(parent) - , d(new Private(this)) + , d(new Private) { } void AppMenu::Private::setup(org_kde_kwin_appmenu *arg) { Q_ASSERT(arg); Q_ASSERT(!appmenu); appmenu.setup(arg); } AppMenu::~AppMenu() { release(); } void AppMenu::setup(org_kde_kwin_appmenu *appmenu) { d->setup(appmenu); } void AppMenu::release() { d->appmenu.release(); } void AppMenu::destroy() { d->appmenu.destroy(); } AppMenu::operator org_kde_kwin_appmenu*() { return d->appmenu; } AppMenu::operator org_kde_kwin_appmenu*() const { return d->appmenu; } bool AppMenu::isValid() const { return d->appmenu.isValid(); } void AppMenu::setAddress(const QString &serviceName, const QString &objectPath) { Q_ASSERT(isValid()); org_kde_kwin_appmenu_set_address(d->appmenu, serviceName.toLatin1(), objectPath.toLatin1()); } } } diff --git a/src/client/event_queue.cpp b/src/client/event_queue.cpp index 9921682..f6c037b 100644 --- a/src/client/event_queue.cpp +++ b/src/client/event_queue.cpp @@ -1,103 +1,93 @@ /* SPDX-FileCopyrightText: 2014 Martin Gräßlin SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ #include "event_queue.h" #include "connection_thread.h" #include "wayland_pointer_p.h" #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN EventQueue::Private { public: - Private(EventQueue *q); - wl_display *display = nullptr; WaylandPointer queue; - -private: - EventQueue *q; }; -EventQueue::Private::Private(EventQueue *q) - : q(q) -{ -} - EventQueue::EventQueue(QObject *parent) : QObject(parent) - , d(new Private(this)) + , d(new Private) { } EventQueue::~EventQueue() { release(); } void EventQueue::release() { d->queue.release(); d->display = nullptr; } void EventQueue::destroy() { d->queue.destroy(); d->display = nullptr; } bool EventQueue::isValid() { return d->queue.isValid(); } void EventQueue::setup(wl_display *display) { Q_ASSERT(display); Q_ASSERT(!d->display); Q_ASSERT(!d->queue); d->display = display; d->queue.setup(wl_display_create_queue(display)); } void EventQueue::setup(ConnectionThread *connection) { setup(connection->display()); connect(connection, &ConnectionThread::eventsRead, this, &EventQueue::dispatch, Qt::QueuedConnection); } void EventQueue::dispatch() { if (!d->display || !d->queue) { return; } wl_display_dispatch_queue_pending(d->display, d->queue); wl_display_flush(d->display); } void EventQueue::addProxy(wl_proxy *proxy) { Q_ASSERT(d->queue); wl_proxy_set_queue(proxy, d->queue); } EventQueue::operator wl_event_queue*() const { return d->queue; } EventQueue::operator wl_event_queue*() { return d->queue; } } } diff --git a/src/client/idleinhibit.cpp b/src/client/idleinhibit.cpp index 36f30ec..b76e1ea 100644 --- a/src/client/idleinhibit.cpp +++ b/src/client/idleinhibit.cpp @@ -1,162 +1,152 @@ /* SPDX-FileCopyrightText: 2017 Martin Flöser SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ #include "idleinhibit.h" #include "event_queue.h" #include "surface.h" #include "wayland_pointer_p.h" #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN IdleInhibitManager::Private { public: Private() = default; void setup(zwp_idle_inhibit_manager_v1 *arg); WaylandPointer idleinhibitmanager; EventQueue *queue = nullptr; }; IdleInhibitManager::IdleInhibitManager(QObject *parent) : QObject(parent) , d(new Private) { } void IdleInhibitManager::Private::setup(zwp_idle_inhibit_manager_v1 *arg) { Q_ASSERT(arg); Q_ASSERT(!idleinhibitmanager); idleinhibitmanager.setup(arg); } IdleInhibitManager::~IdleInhibitManager() { release(); } void IdleInhibitManager::setup(zwp_idle_inhibit_manager_v1 *idleinhibitmanager) { d->setup(idleinhibitmanager); } void IdleInhibitManager::release() { d->idleinhibitmanager.release(); } void IdleInhibitManager::destroy() { d->idleinhibitmanager.destroy(); } IdleInhibitManager::operator zwp_idle_inhibit_manager_v1*() { return d->idleinhibitmanager; } IdleInhibitManager::operator zwp_idle_inhibit_manager_v1*() const { return d->idleinhibitmanager; } bool IdleInhibitManager::isValid() const { return d->idleinhibitmanager.isValid(); } void IdleInhibitManager::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *IdleInhibitManager::eventQueue() { return d->queue; } IdleInhibitor *IdleInhibitManager::createInhibitor(Surface *surface, QObject *parent) { Q_ASSERT(isValid()); auto p = new IdleInhibitor(parent); auto w = zwp_idle_inhibit_manager_v1_create_inhibitor(d->idleinhibitmanager, *surface); if (d->queue) { d->queue->addProxy(w); } p->setup(w); return p; } class Q_DECL_HIDDEN IdleInhibitor::Private { public: - Private(IdleInhibitor *q); - void setup(zwp_idle_inhibitor_v1 *arg); WaylandPointer idleinhibitor; - -private: - IdleInhibitor *q; }; -IdleInhibitor::Private::Private(IdleInhibitor *q) - : q(q) -{ -} - IdleInhibitor::IdleInhibitor(QObject *parent) : QObject(parent) - , d(new Private(this)) + , d(new Private) { } void IdleInhibitor::Private::setup(zwp_idle_inhibitor_v1 *arg) { Q_ASSERT(arg); Q_ASSERT(!idleinhibitor); idleinhibitor.setup(arg); } IdleInhibitor::~IdleInhibitor() { release(); } void IdleInhibitor::setup(zwp_idle_inhibitor_v1 *idleinhibitor) { d->setup(idleinhibitor); } void IdleInhibitor::release() { d->idleinhibitor.release(); } void IdleInhibitor::destroy() { d->idleinhibitor.destroy(); } IdleInhibitor::operator zwp_idle_inhibitor_v1*() { return d->idleinhibitor; } IdleInhibitor::operator zwp_idle_inhibitor_v1*() const { return d->idleinhibitor; } bool IdleInhibitor::isValid() const { return d->idleinhibitor.isValid(); } } } diff --git a/src/client/server_decoration_palette.cpp b/src/client/server_decoration_palette.cpp index 2d37a60..e19db46 100644 --- a/src/client/server_decoration_palette.cpp +++ b/src/client/server_decoration_palette.cpp @@ -1,170 +1,160 @@ /* SPDX-FileCopyrightText: 2017 David Edmundson SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ #include "server_decoration_palette.h" #include "event_queue.h" #include "surface.h" #include "wayland_pointer_p.h" #include #include namespace KWayland { namespace Client { class ServerSideDecorationPaletteManager::Private { public: Private() = default; void setup(org_kde_kwin_server_decoration_palette_manager *arg); WaylandPointer serverdecomanager; EventQueue *queue = nullptr; }; ServerSideDecorationPaletteManager::ServerSideDecorationPaletteManager(QObject *parent) : QObject(parent) , d(new Private) { } void ServerSideDecorationPaletteManager::Private::setup(org_kde_kwin_server_decoration_palette_manager *arg) { Q_ASSERT(arg); Q_ASSERT(!serverdecomanager); serverdecomanager.setup(arg); } ServerSideDecorationPaletteManager::~ServerSideDecorationPaletteManager() { release(); } void ServerSideDecorationPaletteManager::setup(org_kde_kwin_server_decoration_palette_manager *serverdecomanager) { d->setup(serverdecomanager); } void ServerSideDecorationPaletteManager::release() { d->serverdecomanager.release(); } void ServerSideDecorationPaletteManager::destroy() { d->serverdecomanager.destroy(); } ServerSideDecorationPaletteManager::operator org_kde_kwin_server_decoration_palette_manager*() { return d->serverdecomanager; } ServerSideDecorationPaletteManager::operator org_kde_kwin_server_decoration_palette_manager*() const { return d->serverdecomanager; } bool ServerSideDecorationPaletteManager::isValid() const { return d->serverdecomanager.isValid(); } void ServerSideDecorationPaletteManager::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *ServerSideDecorationPaletteManager::eventQueue() { return d->queue; } ServerSideDecorationPalette *ServerSideDecorationPaletteManager::create(Surface *surface, QObject *parent) { Q_ASSERT(isValid()); auto p = new ServerSideDecorationPalette(parent); auto w = org_kde_kwin_server_decoration_palette_manager_create(d->serverdecomanager, *surface); if (d->queue) { d->queue->addProxy(w); } p->setup(w); return p; } class ServerSideDecorationPalette::Private { public: - Private(ServerSideDecorationPalette *q); - void setup(org_kde_kwin_server_decoration_palette *arg); WaylandPointer decoration_palette; - -private: - ServerSideDecorationPalette *q; }; -ServerSideDecorationPalette::Private::Private(ServerSideDecorationPalette *q) - : q(q) -{ -} - ServerSideDecorationPalette::ServerSideDecorationPalette(QObject *parent) : QObject(parent) - , d(new Private(this)) + , d(new Private) { } void ServerSideDecorationPalette::Private::setup(org_kde_kwin_server_decoration_palette *arg) { Q_ASSERT(arg); Q_ASSERT(!decoration_palette); decoration_palette.setup(arg); } ServerSideDecorationPalette::~ServerSideDecorationPalette() { release(); } void ServerSideDecorationPalette::setup(org_kde_kwin_server_decoration_palette *decoration_palette) { d->setup(decoration_palette); } void ServerSideDecorationPalette::release() { d->decoration_palette.release(); } void ServerSideDecorationPalette::destroy() { d->decoration_palette.destroy(); } ServerSideDecorationPalette::operator org_kde_kwin_server_decoration_palette*() { return d->decoration_palette; } ServerSideDecorationPalette::operator org_kde_kwin_server_decoration_palette*() const { return d->decoration_palette; } bool ServerSideDecorationPalette::isValid() const { return d->decoration_palette.isValid(); } void ServerSideDecorationPalette::setPalette(const QString &palette) { Q_ASSERT(isValid()); org_kde_kwin_server_decoration_palette_set_palette(*this, palette.toUtf8()); } } } diff --git a/src/server/buffer_interface.cpp b/src/server/buffer_interface.cpp index c21321c..dc1b396 100644 --- a/src/server/buffer_interface.cpp +++ b/src/server/buffer_interface.cpp @@ -1,339 +1,339 @@ /* SPDX-FileCopyrightText: 2014 Martin Gräßlin SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ #include "buffer_interface.h" #include "display.h" #include "logging.h" #include "surface_interface.h" #include "linuxdmabuf_v1_interface.h" // Wayland #include // EGL #include #include #include "drm_fourcc.h" namespace KWayland { namespace Server { namespace EGL { typedef GLboolean(*eglQueryWaylandBufferWL_func)(EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value); eglQueryWaylandBufferWL_func eglQueryWaylandBufferWL = nullptr; } class BufferInterface::Private { public: Private(BufferInterface *q, wl_resource *resource, SurfaceInterface *parent); ~Private(); QImage::Format format() const; QImage createImage(); wl_resource *buffer; wl_shm_buffer *shmBuffer; LinuxDmabufBuffer *dmabufBuffer; SurfaceInterface *surface; int refCount; QSize size; bool alpha; static BufferInterface *get(wl_resource *r); private: static void destroyListenerCallback(wl_listener *listener, void *data); static Private *cast(wl_resource *r); static void imageBufferCleanupHandler(void *info); static QList s_buffers; static Private *s_accessedBuffer; static int s_accessCounter; BufferInterface *q; wl_listener listener; }; QList BufferInterface::Private::s_buffers; BufferInterface::Private *BufferInterface::Private::s_accessedBuffer = nullptr; int BufferInterface::Private::s_accessCounter = 0; BufferInterface::Private *BufferInterface::Private::cast(wl_resource *r) { auto it = std::find_if(s_buffers.constBegin(), s_buffers.constEnd(), [r](Private *d) { return d->buffer == r; }); if (it == s_buffers.constEnd()) { return nullptr; } return *it; } BufferInterface *BufferInterface::Private::get(wl_resource *r) { Private *p = cast(r); if (!p) { return nullptr; } return p->q; } void BufferInterface::Private::imageBufferCleanupHandler(void *info) { Private *p = reinterpret_cast(info); Q_ASSERT(p == s_accessedBuffer); Q_ASSERT(s_accessCounter > 0); s_accessCounter--; if (s_accessCounter == 0) { s_accessedBuffer = nullptr; } wl_shm_buffer_end_access(p->shmBuffer); } BufferInterface::Private::Private(BufferInterface *q, wl_resource *resource, SurfaceInterface *parent) : buffer(resource) , shmBuffer(wl_shm_buffer_get(resource)) , dmabufBuffer(nullptr) , surface(parent) , refCount(0) , alpha(false) , q(q) { if (!shmBuffer && wl_resource_instance_of(resource, &wl_buffer_interface, LinuxDmabufUnstableV1Interface::bufferImplementation())) { dmabufBuffer = static_cast(wl_resource_get_user_data(resource)); } s_buffers << this; listener.notify = destroyListenerCallback; listener.link.prev = nullptr; listener.link.next = nullptr; wl_resource_add_destroy_listener(resource, &listener); if (shmBuffer) { size = QSize(wl_shm_buffer_get_width(shmBuffer), wl_shm_buffer_get_height(shmBuffer)); // check alpha switch (wl_shm_buffer_get_format(shmBuffer)) { case WL_SHM_FORMAT_ARGB8888: alpha = true; break; case WL_SHM_FORMAT_XRGB8888: default: alpha = false; break; } } else if (dmabufBuffer) { switch (dmabufBuffer->format()) { case DRM_FORMAT_ARGB4444: case DRM_FORMAT_ABGR4444: case DRM_FORMAT_RGBA4444: case DRM_FORMAT_BGRA4444: case DRM_FORMAT_ARGB1555: case DRM_FORMAT_ABGR1555: case DRM_FORMAT_RGBA5551: case DRM_FORMAT_BGRA5551: case DRM_FORMAT_ARGB8888: case DRM_FORMAT_ABGR8888: case DRM_FORMAT_RGBA8888: case DRM_FORMAT_BGRA8888: case DRM_FORMAT_ARGB2101010: case DRM_FORMAT_ABGR2101010: case DRM_FORMAT_RGBA1010102: case DRM_FORMAT_BGRA1010102: case DRM_FORMAT_XRGB8888_A8: case DRM_FORMAT_XBGR8888_A8: case DRM_FORMAT_RGBX8888_A8: case DRM_FORMAT_BGRX8888_A8: case DRM_FORMAT_RGB888_A8: case DRM_FORMAT_BGR888_A8: case DRM_FORMAT_RGB565_A8: case DRM_FORMAT_BGR565_A8: alpha = true; break; default: alpha = false; break; } size = dmabufBuffer->size(); } else if (parent) { EGLDisplay eglDisplay = parent->global()->display()->eglDisplay(); static bool resolved = false; using namespace EGL; if (!resolved && eglDisplay != EGL_NO_DISPLAY) { eglQueryWaylandBufferWL = (eglQueryWaylandBufferWL_func)eglGetProcAddress("eglQueryWaylandBufferWL"); resolved = true; } if (eglQueryWaylandBufferWL) { EGLint width, height; bool valid = false; valid = eglQueryWaylandBufferWL(eglDisplay, buffer, EGL_WIDTH, &width); valid = valid && eglQueryWaylandBufferWL(eglDisplay, buffer, EGL_HEIGHT, &height); if (valid) { size = QSize(width, height); } // check alpha EGLint format; if (eglQueryWaylandBufferWL(eglDisplay, buffer, EGL_TEXTURE_FORMAT, &format)) { switch (format) { case EGL_TEXTURE_RGBA: alpha = true; break; case EGL_TEXTURE_RGB: default: alpha = false; break; } } } } } BufferInterface::Private::~Private() { wl_list_remove(&listener.link); s_buffers.removeAll(this); } BufferInterface *BufferInterface::get(wl_resource *r) { if (!r) { return nullptr; } // TODO: verify it's a buffer BufferInterface *b = Private::get(r); if (b) { return b; } return new BufferInterface(r, nullptr); } BufferInterface::BufferInterface(wl_resource *resource, SurfaceInterface *parent) : QObject() , d(new Private(this, resource, parent)) { } BufferInterface::~BufferInterface() { if (d->refCount != 0) { qCWarning(KWAYLAND_SERVER) << "Buffer destroyed while still being referenced, ref count:" << d->refCount; } } void BufferInterface::Private::destroyListenerCallback(wl_listener *listener, void *data) { Q_UNUSED(listener); auto b = cast(reinterpret_cast(data)); b->buffer = nullptr; emit b->q->aboutToBeDestroyed(b->q); delete b->q; } void BufferInterface::ref() { d->refCount++; } void BufferInterface::unref() { Q_ASSERT(d->refCount > 0); d->refCount--; if (d->refCount == 0) { if (d->buffer) { wl_buffer_send_release(d->buffer); wl_client_flush(wl_resource_get_client(d->buffer)); } deleteLater(); } } QImage::Format BufferInterface::Private::format() const { if (!shmBuffer) { return QImage::Format_Invalid; } switch (wl_shm_buffer_get_format(shmBuffer)) { case WL_SHM_FORMAT_ARGB8888: return QImage::Format_ARGB32_Premultiplied; case WL_SHM_FORMAT_XRGB8888: return QImage::Format_RGB32; default: return QImage::Format_Invalid; } } QImage BufferInterface::data() { - return std::move(d->createImage()); + return d->createImage(); } QImage BufferInterface::Private::createImage() { if (!shmBuffer) { return QImage(); } if (s_accessedBuffer != nullptr && s_accessedBuffer != this) { return QImage(); } const QImage::Format imageFormat = format(); if (imageFormat == QImage::Format_Invalid) { return QImage(); } s_accessedBuffer = this; s_accessCounter++; wl_shm_buffer_begin_access(shmBuffer); - return std::move(QImage((const uchar*)wl_shm_buffer_get_data(shmBuffer), - size.width(), - size.height(), - wl_shm_buffer_get_stride(shmBuffer), - imageFormat, - &imageBufferCleanupHandler, this)); + return QImage((const uchar*)wl_shm_buffer_get_data(shmBuffer), + size.width(), + size.height(), + wl_shm_buffer_get_stride(shmBuffer), + imageFormat, + &imageBufferCleanupHandler, this); } bool BufferInterface::isReferenced() const { return d->refCount > 0; } SurfaceInterface *BufferInterface::surface() const { return d->surface; } wl_shm_buffer *BufferInterface::shmBuffer() { return d->shmBuffer; } LinuxDmabufBuffer *BufferInterface::linuxDmabufBuffer() { return d->dmabufBuffer; } wl_resource *BufferInterface::resource() const { return d->buffer; } QSize BufferInterface::size() const { return d->size; } void BufferInterface::setSize(const QSize &size) { if (d->shmBuffer || d->size == size) { return; } d->size = size; emit sizeChanged(); } bool BufferInterface::hasAlphaChannel() const { return d->alpha; } } } diff --git a/src/server/display.cpp b/src/server/display.cpp index 2845e76..3592db1 100644 --- a/src/server/display.cpp +++ b/src/server/display.cpp @@ -1,650 +1,650 @@ /* SPDX-FileCopyrightText: 2014 Martin Gräßlin SPDX-FileCopyrightText: 2018 David Edmundson SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ #include "display.h" #include "appmenu_interface.h" #include "blur_interface.h" #include "compositor_interface.h" #include "contrast_interface.h" #include "datadevicemanager_interface.h" #include "dpms_interface.h" #include "eglstream_controller_interface.h" #include "fakeinput_interface.h" #include "idle_interface.h" #include "idleinhibit_interface_p.h" #include "keystate_interface.h" #include "linuxdmabuf_v1_interface.h" #include "logging.h" #include "output_interface.h" #include "outputconfiguration_interface.h" #include "outputdevice_interface.h" #include "outputmanagement_interface.h" #include "plasmashell_interface.h" #include "plasmavirtualdesktop_interface.h" #include "plasmawindowmanagement_interface.h" #include "pointerconstraints_interface_p.h" #include "pointergestures_interface_p.h" #include "qtsurfaceextension_interface.h" #include "relativepointer_interface_p.h" #include "remote_access_interface.h" #include "seat_interface.h" #include "server_decoration_interface.h" #include "server_decoration_palette_interface.h" #include "shadow_interface.h" #include "shell_interface.h" #include "slide_interface.h" #include "subcompositor_interface.h" #include "tablet_interface.h" #include "textinput_interface_p.h" #include "xdgdecoration_interface.h" #include "xdgforeign_interface.h" #include "xdgoutput_interface.h" #include "xdgshell_stable_interface_p.h" #include "xdgshell_v5_interface_p.h" #include "xdgshell_v6_interface_p.h" #include #include #include #include #include #include #include namespace KWayland { namespace Server { class Display::Private { public: Private(Display *q); void flush(); void dispatch(); void setRunning(bool running); void installSocketNotifier(); wl_display *display = nullptr; wl_event_loop *loop = nullptr; QString socketName = QStringLiteral("wayland-0"); bool running = false; bool automaticSocketNaming = false; QList outputs; QList outputdevices; QVector seats; QVector clients; EGLDisplay eglDisplay = EGL_NO_DISPLAY; private: Display *q; }; Display::Private::Private(Display *q) : q(q) { } void Display::Private::installSocketNotifier() { if (!QThread::currentThread()) { return; } int fd = wl_event_loop_get_fd(loop); if (fd == -1) { qCWarning(KWAYLAND_SERVER) << "Did not get the file descriptor for the event loop"; return; } QSocketNotifier *m_notifier = new QSocketNotifier(fd, QSocketNotifier::Read, q); QObject::connect(m_notifier, &QSocketNotifier::activated, q, [this] { dispatch(); } ); QObject::connect(QThread::currentThread()->eventDispatcher(), &QAbstractEventDispatcher::aboutToBlock, q, [this] { flush(); }); setRunning(true); } Display::Display(QObject *parent) : QObject(parent) , d(new Private(this)) { } Display::~Display() { terminate(); if (d->display) { wl_display_destroy(d->display); } } void Display::Private::flush() { if (!display || !loop) { return; } wl_display_flush_clients(display); } void Display::Private::dispatch() { if (!display || !loop) { return; } if (wl_event_loop_dispatch(loop, 0) != 0) { qCWarning(KWAYLAND_SERVER) << "Error on dispatching Wayland event loop"; } } void Display::setSocketName(const QString &name) { if (d->socketName == name) { return; } d->socketName = name; emit socketNameChanged(d->socketName); } QString Display::socketName() const { return d->socketName; } void Display::setAutomaticSocketNaming(bool automaticSocketNaming) { if (d->automaticSocketNaming == automaticSocketNaming) { return; } d->automaticSocketNaming = automaticSocketNaming; emit automaticSocketNamingChanged(automaticSocketNaming); } bool Display::automaticSocketNaming() const { return d->automaticSocketNaming; } void Display::start(StartMode mode) { Q_ASSERT(!d->running); Q_ASSERT(!d->display); d->display = wl_display_create(); if (mode == StartMode::ConnectToSocket) { if (d->automaticSocketNaming) { const char *socket = wl_display_add_socket_auto(d->display); if (socket == nullptr) { qCWarning(KWAYLAND_SERVER) << "Failed to create Wayland socket"; return; } const QString newEffectiveSocketName = QString::fromUtf8(socket); if (d->socketName != newEffectiveSocketName) { d->socketName = newEffectiveSocketName; emit socketNameChanged(d->socketName); } } else if (wl_display_add_socket(d->display, qPrintable(d->socketName)) != 0) { qCWarning(KWAYLAND_SERVER) << "Failed to create Wayland socket"; return; } } d->loop = wl_display_get_event_loop(d->display); d->installSocketNotifier(); } void Display::startLoop() { Q_ASSERT(!d->running); Q_ASSERT(d->display); d->installSocketNotifier(); } void Display::dispatchEvents(int msecTimeout) { Q_ASSERT(d->display); if (d->running) { d->dispatch(); } else if (d->loop) { wl_event_loop_dispatch(d->loop, msecTimeout); wl_display_flush_clients(d->display); } } void Display::terminate() { if (!d->running) { return; } emit aboutToTerminate(); wl_display_terminate(d->display); wl_display_destroy(d->display); d->display = nullptr; d->loop = nullptr; d->setRunning(false); } void Display::Private::setRunning(bool r) { Q_ASSERT(running != r); running = r; emit q->runningChanged(running); } OutputInterface *Display::createOutput(QObject *parent) { OutputInterface *output = new OutputInterface(this, parent); connect(output, &QObject::destroyed, this, [this,output] { d->outputs.removeAll(output); }); connect(this, &Display::aboutToTerminate, output, [this,output] { removeOutput(output); }); d->outputs << output; return output; } CompositorInterface *Display::createCompositor(QObject *parent) { CompositorInterface *compositor = new CompositorInterface(this, parent); - connect(this, &Display::aboutToTerminate, compositor, [this,compositor] { delete compositor; }); + connect(this, &Display::aboutToTerminate, compositor, [compositor] { delete compositor; }); return compositor; } ShellInterface *Display::createShell(QObject *parent) { ShellInterface *shell = new ShellInterface(this, parent); - connect(this, &Display::aboutToTerminate, shell, [this,shell] { delete shell; }); + connect(this, &Display::aboutToTerminate, shell, [shell] { delete shell; }); return shell; } OutputDeviceInterface *Display::createOutputDevice(QObject *parent) { OutputDeviceInterface *output = new OutputDeviceInterface(this, parent); connect(output, &QObject::destroyed, this, [this,output] { d->outputdevices.removeAll(output); }); connect(this, &Display::aboutToTerminate, output, [this,output] { removeOutputDevice(output); }); d->outputdevices << output; return output; } OutputManagementInterface *Display::createOutputManagement(QObject *parent) { OutputManagementInterface *om = new OutputManagementInterface(this, parent); - connect(this, &Display::aboutToTerminate, om, [this,om] { delete om; }); + connect(this, &Display::aboutToTerminate, om, [om] { delete om; }); return om; } SeatInterface *Display::createSeat(QObject *parent) { SeatInterface *seat = new SeatInterface(this, parent); connect(seat, &QObject::destroyed, this, [this, seat] { d->seats.removeAll(seat); }); - connect(this, &Display::aboutToTerminate, seat, [this,seat] { delete seat; }); + connect(this, &Display::aboutToTerminate, seat, [seat] { delete seat; }); d->seats << seat; return seat; } SubCompositorInterface *Display::createSubCompositor(QObject *parent) { auto c = new SubCompositorInterface(this, parent); - connect(this, &Display::aboutToTerminate, c, [this,c] { delete c; }); + connect(this, &Display::aboutToTerminate, c, [c] { delete c; }); return c; } DataDeviceManagerInterface *Display::createDataDeviceManager(QObject *parent) { auto m = new DataDeviceManagerInterface(this, parent); - connect(this, &Display::aboutToTerminate, m, [this,m] { delete m; }); + connect(this, &Display::aboutToTerminate, m, [m] { delete m; }); return m; } PlasmaShellInterface *Display::createPlasmaShell(QObject* parent) { auto s = new PlasmaShellInterface(this, parent); - connect(this, &Display::aboutToTerminate, s, [this, s] { delete s; }); + connect(this, &Display::aboutToTerminate, s, [s] { delete s; }); return s; } PlasmaWindowManagementInterface *Display::createPlasmaWindowManagement(QObject *parent) { auto wm = new PlasmaWindowManagementInterface(this, parent); - connect(this, &Display::aboutToTerminate, wm, [this, wm] { delete wm; }); + connect(this, &Display::aboutToTerminate, wm, [wm] { delete wm; }); return wm; } QtSurfaceExtensionInterface *Display::createQtSurfaceExtension(QObject *parent) { auto s = new QtSurfaceExtensionInterface(this, parent); - connect(this, &Display::aboutToTerminate, s, [this, s] { delete s; }); + connect(this, &Display::aboutToTerminate, s, [s] { delete s; }); return s; } RemoteAccessManagerInterface *Display::createRemoteAccessManager(QObject *parent) { auto i = new RemoteAccessManagerInterface(this, parent); - connect(this, &Display::aboutToTerminate, i, [this, i] { delete i; }); + connect(this, &Display::aboutToTerminate, i, [i] { delete i; }); return i; } IdleInterface *Display::createIdle(QObject *parent) { auto i = new IdleInterface(this, parent); - connect(this, &Display::aboutToTerminate, i, [this, i] { delete i; }); + connect(this, &Display::aboutToTerminate, i, [i] { delete i; }); return i; } FakeInputInterface *Display::createFakeInput(QObject *parent) { auto i = new FakeInputInterface(this, parent); - connect(this, &Display::aboutToTerminate, i, [this, i] { delete i; }); + connect(this, &Display::aboutToTerminate, i, [i] { delete i; }); return i; } ShadowManagerInterface *Display::createShadowManager(QObject *parent) { auto s = new ShadowManagerInterface(this, parent); - connect(this, &Display::aboutToTerminate, s, [this, s] { delete s; }); + connect(this, &Display::aboutToTerminate, s, [s] { delete s; }); return s; } BlurManagerInterface *Display::createBlurManager(QObject *parent) { auto b = new BlurManagerInterface(this, parent); - connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; }); + connect(this, &Display::aboutToTerminate, b, [b] { delete b; }); return b; } ContrastManagerInterface *Display::createContrastManager(QObject *parent) { auto b = new ContrastManagerInterface(this, parent); - connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; }); + connect(this, &Display::aboutToTerminate, b, [b] { delete b; }); return b; } SlideManagerInterface *Display::createSlideManager(QObject *parent) { auto b = new SlideManagerInterface(this, parent); - connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; }); + connect(this, &Display::aboutToTerminate, b, [b] { delete b; }); return b; } DpmsManagerInterface *Display::createDpmsManager(QObject *parent) { auto d = new DpmsManagerInterface(this, parent); - connect(this, &Display::aboutToTerminate, d, [this, d] { delete d; }); + connect(this, &Display::aboutToTerminate, d, [d] { delete d; }); return d; } ServerSideDecorationManagerInterface *Display::createServerSideDecorationManager(QObject *parent) { auto d = new ServerSideDecorationManagerInterface(this, parent); connect(this, &Display::aboutToTerminate, d, [d] { delete d; }); return d; } TextInputManagerInterface *Display::createTextInputManager(const TextInputInterfaceVersion &version, QObject *parent) { TextInputManagerInterface *t = nullptr; switch (version) { case TextInputInterfaceVersion::UnstableV0: t = new TextInputManagerUnstableV0Interface(this, parent); break; case TextInputInterfaceVersion::UnstableV1: // unsupported return nullptr; case TextInputInterfaceVersion::UnstableV2: t = new TextInputManagerUnstableV2Interface(this, parent); } connect(this, &Display::aboutToTerminate, t, [t] { delete t; }); return t; } XdgShellInterface *Display::createXdgShell(const XdgShellInterfaceVersion &version, QObject *parent) { XdgShellInterface *x = nullptr; switch (version) { case XdgShellInterfaceVersion::UnstableV5: x = new XdgShellV5Interface(this, parent); break; case XdgShellInterfaceVersion::UnstableV6: x = new XdgShellV6Interface(this, parent); break; case XdgShellInterfaceVersion::Stable: x = new XdgShellStableInterface(this, parent); break; } connect(this, &Display::aboutToTerminate, x, [x] { delete x; }); return x; } RelativePointerManagerInterface *Display::createRelativePointerManager(const RelativePointerInterfaceVersion &version, QObject *parent) { RelativePointerManagerInterface *r = nullptr; switch (version) { case RelativePointerInterfaceVersion::UnstableV1: r = new RelativePointerManagerUnstableV1Interface(this, parent); break; } connect(this, &Display::aboutToTerminate, r, [r] { delete r; }); return r; } PointerGesturesInterface *Display::createPointerGestures(const PointerGesturesInterfaceVersion &version, QObject *parent) { PointerGesturesInterface *p = nullptr; switch (version) { case PointerGesturesInterfaceVersion::UnstableV1: p = new PointerGesturesUnstableV1Interface(this, parent); break; } connect(this, &Display::aboutToTerminate, p, [p] { delete p; }); return p; } PointerConstraintsInterface *Display::createPointerConstraints(const PointerConstraintsInterfaceVersion &version, QObject *parent) { PointerConstraintsInterface *p = nullptr; switch (version) { case PointerConstraintsInterfaceVersion::UnstableV1: p = new PointerConstraintsUnstableV1Interface(this, parent); break; } connect(this, &Display::aboutToTerminate, p, [p] { delete p; }); return p; } XdgForeignInterface *Display::createXdgForeignInterface(QObject *parent) { XdgForeignInterface *foreign = new XdgForeignInterface(this, parent); - connect(this, &Display::aboutToTerminate, foreign, [this,foreign] { delete foreign; }); + connect(this, &Display::aboutToTerminate, foreign, [foreign] { delete foreign; }); return foreign; } IdleInhibitManagerInterface *Display::createIdleInhibitManager(const IdleInhibitManagerInterfaceVersion &version, QObject *parent) { IdleInhibitManagerInterface *i = nullptr; switch (version) { case IdleInhibitManagerInterfaceVersion::UnstableV1: i = new IdleInhibitManagerUnstableV1Interface(this, parent); break; } - connect(this, &Display::aboutToTerminate, i, [this,i] { delete i; }); + connect(this, &Display::aboutToTerminate, i, [i] { delete i; }); return i; } AppMenuManagerInterface *Display::createAppMenuManagerInterface(QObject *parent) { auto b = new AppMenuManagerInterface(this, parent); - connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; }); + connect(this, &Display::aboutToTerminate, b, [b] { delete b; }); return b; } ServerSideDecorationPaletteManagerInterface *Display::createServerSideDecorationPaletteManager(QObject *parent) { auto b = new ServerSideDecorationPaletteManagerInterface(this, parent); - connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; }); + connect(this, &Display::aboutToTerminate, b, [b] { delete b; }); return b; } LinuxDmabufUnstableV1Interface *Display::createLinuxDmabufInterface(QObject *parent) { auto b = new LinuxDmabufUnstableV1Interface(this, parent); - connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; }); + connect(this, &Display::aboutToTerminate, b, [b] { delete b; }); return b; } PlasmaVirtualDesktopManagementInterface *Display::createPlasmaVirtualDesktopManagement(QObject *parent) { auto b = new PlasmaVirtualDesktopManagementInterface(this, parent); - connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; }); + connect(this, &Display::aboutToTerminate, b, [b] { delete b; }); return b; } XdgOutputManagerInterface *Display::createXdgOutputManager(QObject *parent) { auto b = new XdgOutputManagerInterface(this, parent); - connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; }); + connect(this, &Display::aboutToTerminate, b, [b] { delete b; }); return b; } XdgDecorationManagerInterface *Display::createXdgDecorationManager(XdgShellInterface *shellInterface, QObject *parent) { auto d = new XdgDecorationManagerInterface(this, shellInterface, parent); connect(this, &Display::aboutToTerminate, d, [d] { delete d; }); return d; } EglStreamControllerInterface *Display::createEglStreamControllerInterface(QObject *parent) { EglStreamControllerInterface *e = new EglStreamControllerInterface(this, parent); connect(this, &Display::aboutToTerminate, e, [e] { delete e; }); return e; } KeyStateInterface *Display::createKeyStateInterface(QObject *parent) { auto d = new KeyStateInterface(this, parent); connect(this, &Display::aboutToTerminate, d, [d] { delete d; }); return d; } TabletManagerInterface *Display::createTabletManagerInterface(QObject *parent) { auto d = new TabletManagerInterface(this, parent); connect(this, &Display::aboutToTerminate, d, [d] { delete d; }); return d; } void Display::createShm() { Q_ASSERT(d->display); wl_display_init_shm(d->display); } void Display::removeOutput(OutputInterface *output) { d->outputs.removeAll(output); delete output; } void Display::removeOutputDevice(OutputDeviceInterface *output) { d->outputdevices.removeAll(output); delete output; } quint32 Display::nextSerial() { return wl_display_next_serial(d->display); } quint32 Display::serial() { return wl_display_get_serial(d->display); } bool Display::isRunning() const { return d->running; } Display::operator wl_display*() { return d->display; } Display::operator wl_display*() const { return d->display; } QList< OutputInterface* > Display::outputs() const { return d->outputs; } QList< OutputDeviceInterface* > Display::outputDevices() const { return d->outputdevices; } QVector Display::seats() const { return d->seats; } ClientConnection *Display::getConnection(wl_client *client) { Q_ASSERT(client); auto it = std::find_if(d->clients.constBegin(), d->clients.constEnd(), [client](ClientConnection *c) { return c->client() == client; } ); if (it != d->clients.constEnd()) { return *it; } // no ConnectionData yet, create it auto c = new ClientConnection(client, this); d->clients << c; connect(c, &ClientConnection::disconnected, this, [this] (ClientConnection *c) { const int index = d->clients.indexOf(c); Q_ASSERT(index != -1); d->clients.remove(index); Q_ASSERT(d->clients.indexOf(c) == -1); emit clientDisconnected(c); } ); emit clientConnected(c); return c; } QVector< ClientConnection* > Display::connections() const { return d->clients; } ClientConnection *Display::createClient(int fd) { Q_ASSERT(fd != -1); Q_ASSERT(d->display); wl_client *c = wl_client_create(d->display, fd); if (!c) { return nullptr; } return getConnection(c); } void Display::setEglDisplay(void *display) { if (d->eglDisplay != EGL_NO_DISPLAY) { qCWarning(KWAYLAND_SERVER) << "EGLDisplay cannot be changed"; return; } d->eglDisplay = (EGLDisplay)display; } void *Display::eglDisplay() const { return d->eglDisplay; } } } diff --git a/src/server/eglstream_controller_interface.cpp b/src/server/eglstream_controller_interface.cpp index 079f614..d85f27d 100644 --- a/src/server/eglstream_controller_interface.cpp +++ b/src/server/eglstream_controller_interface.cpp @@ -1,93 +1,93 @@ /* SPDX-FileCopyrightText: 2019 NVIDIA Inc. SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ #include "eglstream_controller_interface_p.h" #include "clientconnection.h" #include "display.h" #include "logging.h" #include #include namespace KWayland { namespace Server { const quint32 EglStreamControllerInterface::Private::s_version = 1; #ifndef K_DOXYGEN const struct wl_eglstream_controller_interface EglStreamControllerInterface::Private::s_interface = { attachStreamConsumer, attachStreamConsumerAttribs }; #endif void EglStreamControllerInterface::Private::attachStreamConsumer(wl_client *client, wl_resource *resource, wl_resource *surface, wl_resource *eglStream) { wl_array noAttribs = { 0, 0, nullptr }; attachStreamConsumerAttribs(client, resource, surface, eglStream, &noAttribs); } void EglStreamControllerInterface::Private::attachStreamConsumerAttribs(wl_client *client, wl_resource *resource, wl_resource *surface, wl_resource *eglStream, wl_array *attribs) { Q_UNUSED(client); Private *p = reinterpret_cast(wl_resource_get_user_data(resource)); emit p->q->streamConsumerAttached(SurfaceInterface::get(surface), eglStream, attribs); } EglStreamControllerInterface::Private::Private(EglStreamControllerInterface *q, Display *display) // libnvidia-egl-wayland.so.1 may not be present on all systems, so we load it dynamically : Global::Private(display, - (wl_interface *)QLibrary::resolve(QLatin1String("libnvidia-egl-wayland.so.1"), - "wl_eglstream_controller_interface"), + reinterpret_cast(QLibrary::resolve(QLatin1String("libnvidia-egl-wayland.so.1"), + "wl_eglstream_controller_interface")), s_version) , q(q) { } void EglStreamControllerInterface::Private::create() { // bail out early if we were unable to load the interface if (m_interface == nullptr) { qCWarning(KWAYLAND_SERVER) << "failed to resolve wl_eglstream_controller_interface"; return; } Global::Private::create(); } void EglStreamControllerInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { wl_resource *r = display->getConnection(client)->createResource(m_interface, version, id); if (r == nullptr) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(r, &s_interface, this, nullptr); } EglStreamControllerInterface::~EglStreamControllerInterface() = default; EglStreamControllerInterface::EglStreamControllerInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } void EglStreamControllerInterface::create() { static_cast(*d).create(); } } } diff --git a/src/server/fakeinput_interface.cpp b/src/server/fakeinput_interface.cpp index b2d9e7e..225aeef 100644 --- a/src/server/fakeinput_interface.cpp +++ b/src/server/fakeinput_interface.cpp @@ -1,314 +1,310 @@ /* SPDX-FileCopyrightText: 2015 Martin Gräßlin SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ #include "fakeinput_interface.h" #include "display.h" #include "global_p.h" #include #include #include #include namespace KWayland { namespace Server { class FakeInputInterface::Private : public Global::Private { public: Private(FakeInputInterface *q, Display *d); QList devices; private: void bind(wl_client *client, uint32_t version, uint32_t id) override; static void authenticateCallback(wl_client *client, wl_resource *resource, const char *application, const char *reason); static void pointerMotionCallback(wl_client *client, wl_resource *resource, wl_fixed_t delta_x, wl_fixed_t delta_y); static void pointerMotionAbsoluteCallback(wl_client *client, wl_resource *resource, wl_fixed_t x, wl_fixed_t y); static void buttonCallback(wl_client *client, wl_resource *resource, uint32_t button, uint32_t state); static void axisCallback(wl_client *client, wl_resource *resource, uint32_t axis, wl_fixed_t value); static void touchDownCallback(wl_client *client, wl_resource *resource, quint32 id, wl_fixed_t x, wl_fixed_t y); static void touchMotionCallback(wl_client *client, wl_resource *resource, quint32 id, wl_fixed_t x, wl_fixed_t y); static void touchUpCallback(wl_client *client, wl_resource *resource, quint32 id); static void touchCancelCallback(wl_client *client, wl_resource *resource); static void touchFrameCallback(wl_client *client, wl_resource *resource); static void keyboardKeyCallback(wl_client *client, wl_resource *resource, uint32_t button, uint32_t state); static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } static FakeInputDevice *device(wl_resource *r); FakeInputInterface *q; static const struct org_kde_kwin_fake_input_interface s_interface; static const quint32 s_version; static QList touchIds; }; class FakeInputDevice::Private { public: - Private(wl_resource *resource, FakeInputInterface *interface, FakeInputDevice *q); + Private(wl_resource *resource, FakeInputInterface *interface); wl_resource *resource; FakeInputInterface *interface; bool authenticated = false; - -private: - FakeInputDevice *q; }; const quint32 FakeInputInterface::Private::s_version = 4; QList FakeInputInterface::Private::touchIds = QList(); #ifndef K_DOXYGEN const struct org_kde_kwin_fake_input_interface FakeInputInterface::Private::s_interface = { authenticateCallback, pointerMotionCallback, buttonCallback, axisCallback, touchDownCallback, touchMotionCallback, touchUpCallback, touchCancelCallback, touchFrameCallback, pointerMotionAbsoluteCallback, keyboardKeyCallback }; #endif FakeInputInterface::Private::Private(FakeInputInterface *q, Display *d) : Global::Private(d, &org_kde_kwin_fake_input_interface, s_version) , q(q) { } void FakeInputInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&org_kde_kwin_fake_input_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); FakeInputDevice *device = new FakeInputDevice(resource, q); devices << device; QObject::connect(device, &FakeInputDevice::destroyed, q, [device, this] { devices.removeAll(device); }); emit q->deviceCreated(device); } void FakeInputInterface::Private::unbind(wl_resource *resource) { if (FakeInputDevice *d = device(resource)) { d->deleteLater(); } } FakeInputDevice *FakeInputInterface::Private::device(wl_resource *r) { Private *p = cast(r); auto it = std::find_if(p->devices.constBegin(), p->devices.constEnd(), [r] (FakeInputDevice *device) { return device->resource() == r; } ); if (it != p->devices.constEnd()) { return *it; } return nullptr; } void FakeInputInterface::Private::authenticateCallback(wl_client *client, wl_resource *resource, const char *application, const char *reason) { Q_UNUSED(client) FakeInputDevice *d = device(resource); if (!d) { return; } emit d->authenticationRequested(QString::fromUtf8(application), QString::fromUtf8(reason)); } void FakeInputInterface::Private::pointerMotionCallback(wl_client *client, wl_resource *resource, wl_fixed_t delta_x, wl_fixed_t delta_y) { Q_UNUSED(client) FakeInputDevice *d = device(resource); if (!d || !d->isAuthenticated()) { return; } emit d->pointerMotionRequested(QSizeF(wl_fixed_to_double(delta_x), wl_fixed_to_double(delta_y))); } void FakeInputInterface::Private::pointerMotionAbsoluteCallback(wl_client *client, wl_resource *resource, wl_fixed_t x, wl_fixed_t y) { Q_UNUSED(client) FakeInputDevice *d = device(resource); if (!d || !d->isAuthenticated()) { return; } emit d->pointerMotionAbsoluteRequested(QPointF(wl_fixed_to_double(x), wl_fixed_to_double(y))); } void FakeInputInterface::Private::axisCallback(wl_client *client, wl_resource *resource, uint32_t axis, wl_fixed_t value) { Q_UNUSED(client) FakeInputDevice *d = device(resource); if (!d || !d->isAuthenticated()) { return; } Qt::Orientation orientation; switch (axis) { case WL_POINTER_AXIS_HORIZONTAL_SCROLL: orientation = Qt::Horizontal; break; case WL_POINTER_AXIS_VERTICAL_SCROLL: orientation = Qt::Vertical; break; default: // invalid return; } emit d->pointerAxisRequested(orientation, wl_fixed_to_double(value)); } void FakeInputInterface::Private::buttonCallback(wl_client *client, wl_resource *resource, uint32_t button, uint32_t state) { Q_UNUSED(client) FakeInputDevice *d = device(resource); if (!d || !d->isAuthenticated()) { return; } switch (state) { case WL_POINTER_BUTTON_STATE_PRESSED: emit d->pointerButtonPressRequested(button); break; case WL_POINTER_BUTTON_STATE_RELEASED: emit d->pointerButtonReleaseRequested(button); break; default: // nothing break; } } void FakeInputInterface::Private::touchDownCallback(wl_client *client, wl_resource *resource, quint32 id, wl_fixed_t x, wl_fixed_t y) { Q_UNUSED(client) FakeInputDevice *d = device(resource); if (!d || !d->isAuthenticated()) { return; } if (touchIds.contains(id)) { return; } touchIds << id; emit d->touchDownRequested(id, QPointF(wl_fixed_to_double(x), wl_fixed_to_double(y))); } void FakeInputInterface::Private::touchMotionCallback(wl_client *client, wl_resource *resource, quint32 id, wl_fixed_t x, wl_fixed_t y) { Q_UNUSED(client) FakeInputDevice *d = device(resource); if (!d || !d->isAuthenticated()) { return; } if (!touchIds.contains(id)) { return; } emit d->touchMotionRequested(id, QPointF(wl_fixed_to_double(x), wl_fixed_to_double(y))); } void FakeInputInterface::Private::touchUpCallback(wl_client *client, wl_resource *resource, quint32 id) { Q_UNUSED(client) FakeInputDevice *d = device(resource); if (!d || !d->isAuthenticated()) { return; } if (!touchIds.contains(id)) { return; } touchIds.removeOne(id); emit d->touchUpRequested(id); } void FakeInputInterface::Private::touchCancelCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) FakeInputDevice *d = device(resource); if (!d || !d->isAuthenticated()) { return; } touchIds.clear(); emit d->touchCancelRequested(); } void FakeInputInterface::Private::touchFrameCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) FakeInputDevice *d = device(resource); if (!d || !d->isAuthenticated()) { return; } emit d->touchFrameRequested(); } void FakeInputInterface::Private::keyboardKeyCallback(wl_client *client, wl_resource *resource, uint32_t button, uint32_t state) { Q_UNUSED(client) FakeInputDevice *d = device(resource); if (!d || !d->isAuthenticated()) { return; } switch (state) { case WL_KEYBOARD_KEY_STATE_PRESSED: emit d->keyboardKeyPressRequested(button); break; case WL_KEYBOARD_KEY_STATE_RELEASED: emit d->keyboardKeyReleaseRequested(button); break; default: // nothing break; } } FakeInputInterface::FakeInputInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } FakeInputInterface::~FakeInputInterface() = default; -FakeInputDevice::Private::Private(wl_resource *resource, FakeInputInterface *interface, FakeInputDevice *q) +FakeInputDevice::Private::Private(wl_resource *resource, FakeInputInterface *interface) : resource(resource) , interface(interface) - , q(q) { } FakeInputDevice::FakeInputDevice(wl_resource *resource, FakeInputInterface *parent) : QObject(parent) - , d(new Private(resource, parent, this)) + , d(new Private(resource, parent)) { } FakeInputDevice::~FakeInputDevice() = default; void FakeInputDevice::setAuthentication(bool authenticated) { d->authenticated = authenticated; } wl_resource *FakeInputDevice::resource() { return d->resource; } bool FakeInputDevice::isAuthenticated() const { return d->authenticated; } } } diff --git a/src/server/output_interface.cpp b/src/server/output_interface.cpp index ddfccd1..2156317 100644 --- a/src/server/output_interface.cpp +++ b/src/server/output_interface.cpp @@ -1,532 +1,533 @@ /* SPDX-FileCopyrightText: 2014 Martin Gräßlin SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ #include "output_interface.h" #include "global_p.h" #include "display.h" #include #include namespace KWayland { namespace Server { class OutputInterface::Private : public Global::Private { public: struct ResourceData { wl_resource *resource; uint32_t version; }; Private(OutputInterface *q, Display *d); ~Private(); void sendMode(wl_resource *resource, const Mode &mode); void sendDone(const ResourceData &data); void updateGeometry(); void updateScale(); QSize physicalSize; QPoint globalPosition; QString manufacturer = QStringLiteral("org.kde.kwin"); QString model = QStringLiteral("none"); int scale = 1; SubPixel subPixel = SubPixel::Unknown; Transform transform = Transform::Normal; QList modes; QList resources; struct { DpmsMode mode = DpmsMode::On; bool supported = false; } dpms; static OutputInterface *get(wl_resource *native); private: static Private *cast(wl_resource *native); static void releaseCallback(wl_client *client, wl_resource *resource); static void unbind(wl_resource *resource); void bind(wl_client *client, uint32_t version, uint32_t id) override; int32_t toTransform() const; int32_t toSubPixel() const; void sendGeometry(wl_resource *resource); void sendScale(const ResourceData &data); OutputInterface *q; static QVector s_privates; static const struct wl_output_interface s_interface; static const quint32 s_version; }; QVector OutputInterface::Private::s_privates; const quint32 OutputInterface::Private::s_version = 3; OutputInterface::Private::Private(OutputInterface *q, Display *d) : Global::Private(d, &wl_output_interface, s_version) , q(q) { s_privates << this; } OutputInterface::Private::~Private() { s_privates.removeAll(this); } #ifndef K_DOXYGEN const struct wl_output_interface OutputInterface::Private::s_interface = { releaseCallback }; #endif void OutputInterface::Private::releaseCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client); unbind(resource); } OutputInterface *OutputInterface::Private::get(wl_resource *native) { if (Private *p = cast(native)) { return p->q; } return nullptr; } OutputInterface::Private *OutputInterface::Private::cast(wl_resource *native) { for (auto it = s_privates.constBegin(); it != s_privates.constEnd(); ++it) { const auto &resources = (*it)->resources; auto rit = std::find_if(resources.constBegin(), resources.constEnd(), [native] (const ResourceData &data) { return data.resource == native; }); if (rit != resources.constEnd()) { return (*it); } } return nullptr; } OutputInterface::OutputInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { Q_D(); connect(this, &OutputInterface::currentModeChanged, this, - [this, d] { + [this] { + Q_D(); auto currentModeIt = std::find_if(d->modes.constBegin(), d->modes.constEnd(), [](const Mode &mode) { return mode.flags.testFlag(ModeFlag::Current); }); if (currentModeIt == d->modes.constEnd()) { return; } for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { d->sendMode((*it).resource, *currentModeIt); d->sendDone(*it); } wl_display_flush_clients(*(d->display)); } ); - connect(this, &OutputInterface::subPixelChanged, this, [this, d] { d->updateGeometry(); }); - connect(this, &OutputInterface::transformChanged, this, [this, d] { d->updateGeometry(); }); - connect(this, &OutputInterface::globalPositionChanged, this, [this, d] { d->updateGeometry(); }); - connect(this, &OutputInterface::modelChanged, this, [this, d] { d->updateGeometry(); }); - connect(this, &OutputInterface::manufacturerChanged, this, [this, d] { d->updateGeometry(); }); - connect(this, &OutputInterface::scaleChanged, this, [this, d] { d->updateScale(); }); + connect(this, &OutputInterface::subPixelChanged, this, [d] { d->updateGeometry(); }); + connect(this, &OutputInterface::transformChanged, this, [d] { d->updateGeometry(); }); + connect(this, &OutputInterface::globalPositionChanged, this, [d] { d->updateGeometry(); }); + connect(this, &OutputInterface::modelChanged, this, [d] { d->updateGeometry(); }); + connect(this, &OutputInterface::manufacturerChanged, this, [d] { d->updateGeometry(); }); + connect(this, &OutputInterface::scaleChanged, this, [d] { d->updateScale(); }); } OutputInterface::~OutputInterface() = default; QSize OutputInterface::pixelSize() const { Q_D(); auto it = std::find_if(d->modes.constBegin(), d->modes.constEnd(), [](const Mode &mode) { return mode.flags.testFlag(ModeFlag::Current); } ); if (it == d->modes.constEnd()) { return QSize(); } return (*it).size; } int OutputInterface::refreshRate() const { Q_D(); auto it = std::find_if(d->modes.constBegin(), d->modes.constEnd(), [](const Mode &mode) { return mode.flags.testFlag(ModeFlag::Current); } ); if (it == d->modes.constEnd()) { return 60000; } return (*it).refreshRate; } void OutputInterface::addMode(const QSize &size, OutputInterface::ModeFlags flags, int refreshRate) { Q_ASSERT(!isValid()); Q_D(); auto currentModeIt = std::find_if(d->modes.begin(), d->modes.end(), [](const Mode &mode) { return mode.flags.testFlag(ModeFlag::Current); } ); if (currentModeIt == d->modes.end() && !flags.testFlag(ModeFlag::Current)) { // no mode with current flag - enforce flags |= ModeFlag::Current; } if (currentModeIt != d->modes.end() && flags.testFlag(ModeFlag::Current)) { // another mode has the current flag - remove (*currentModeIt).flags &= ~uint(ModeFlag::Current); } if (flags.testFlag(ModeFlag::Preferred)) { // remove from existing Preferred mode auto preferredIt = std::find_if(d->modes.begin(), d->modes.end(), [](const Mode &mode) { return mode.flags.testFlag(ModeFlag::Preferred); } ); if (preferredIt != d->modes.end()) { (*preferredIt).flags &= ~uint(ModeFlag::Preferred); } } auto existingModeIt = std::find_if(d->modes.begin(), d->modes.end(), [size,refreshRate](const Mode &mode) { return mode.size == size && mode.refreshRate == refreshRate; } ); auto emitChanges = [this,flags,size,refreshRate] { emit modesChanged(); if (flags.testFlag(ModeFlag::Current)) { emit refreshRateChanged(refreshRate); emit pixelSizeChanged(size); emit currentModeChanged(); } }; if (existingModeIt != d->modes.end()) { if ((*existingModeIt).flags == flags) { // nothing to do return; } (*existingModeIt).flags = flags; emitChanges(); return; } Mode mode; mode.size = size; mode.refreshRate = refreshRate; mode.flags = flags; d->modes << mode; emitChanges(); } void OutputInterface::setCurrentMode(const QSize &size, int refreshRate) { Q_D(); auto currentModeIt = std::find_if(d->modes.begin(), d->modes.end(), [](const Mode &mode) { return mode.flags.testFlag(ModeFlag::Current); } ); if (currentModeIt != d->modes.end()) { // another mode has the current flag - remove (*currentModeIt).flags &= ~uint(ModeFlag::Current); } auto existingModeIt = std::find_if(d->modes.begin(), d->modes.end(), [size,refreshRate](const Mode &mode) { return mode.size == size && mode.refreshRate == refreshRate; } ); Q_ASSERT(existingModeIt != d->modes.end()); (*existingModeIt).flags |= ModeFlag::Current; emit modesChanged(); emit refreshRateChanged((*existingModeIt).refreshRate); emit pixelSizeChanged((*existingModeIt).size); emit currentModeChanged(); } int32_t OutputInterface::Private::toTransform() const { switch (transform) { case Transform::Normal: return WL_OUTPUT_TRANSFORM_NORMAL; case Transform::Rotated90: return WL_OUTPUT_TRANSFORM_90; case Transform::Rotated180: return WL_OUTPUT_TRANSFORM_180; case Transform::Rotated270: return WL_OUTPUT_TRANSFORM_270; case Transform::Flipped: return WL_OUTPUT_TRANSFORM_FLIPPED; case Transform::Flipped90: return WL_OUTPUT_TRANSFORM_FLIPPED_90; case Transform::Flipped180: return WL_OUTPUT_TRANSFORM_FLIPPED_180; case Transform::Flipped270: return WL_OUTPUT_TRANSFORM_FLIPPED_270; } abort(); } int32_t OutputInterface::Private::toSubPixel() const { switch (subPixel) { case SubPixel::Unknown: return WL_OUTPUT_SUBPIXEL_UNKNOWN; case SubPixel::None: return WL_OUTPUT_SUBPIXEL_NONE; case SubPixel::HorizontalRGB: return WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB; case SubPixel::HorizontalBGR: return WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR; case SubPixel::VerticalRGB: return WL_OUTPUT_SUBPIXEL_VERTICAL_RGB; case SubPixel::VerticalBGR: return WL_OUTPUT_SUBPIXEL_VERTICAL_BGR; } abort(); } void OutputInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&wl_output_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_user_data(resource, this); wl_resource_set_implementation(resource, &s_interface, this, unbind); ResourceData r; r.resource = resource; r.version = version; resources << r; sendGeometry(resource); sendScale(r); auto currentModeIt = modes.constEnd(); for (auto it = modes.constBegin(); it != modes.constEnd(); ++it) { const Mode &mode = *it; if (mode.flags.testFlag(ModeFlag::Current)) { // needs to be sent as last mode currentModeIt = it; continue; } sendMode(resource, mode); } if (currentModeIt != modes.constEnd()) { sendMode(resource, *currentModeIt); } sendDone(r); c->flush(); } void OutputInterface::Private::unbind(wl_resource *resource) { Private *o = cast(resource); if (!o) { return; } auto it = std::find_if(o->resources.begin(), o->resources.end(), [resource](const ResourceData &r) { return r.resource == resource; }); if (it != o->resources.end()) { o->resources.erase(it); } } void OutputInterface::Private::sendMode(wl_resource *resource, const Mode &mode) { int32_t flags = 0; if (mode.flags.testFlag(ModeFlag::Current)) { flags |= WL_OUTPUT_MODE_CURRENT; } if (mode.flags.testFlag(ModeFlag::Preferred)) { flags |= WL_OUTPUT_MODE_PREFERRED; } wl_output_send_mode(resource, flags, mode.size.width(), mode.size.height(), mode.refreshRate); } void OutputInterface::Private::sendGeometry(wl_resource *resource) { wl_output_send_geometry(resource, globalPosition.x(), globalPosition.y(), physicalSize.width(), physicalSize.height(), toSubPixel(), qPrintable(manufacturer), qPrintable(model), toTransform()); } void OutputInterface::Private::sendScale(const ResourceData &data) { if (data.version < 2) { return; } wl_output_send_scale(data.resource, scale); } void OutputInterface::Private::sendDone(const ResourceData &data) { if (data.version < 2) { return; } wl_output_send_done(data.resource); } void OutputInterface::Private::updateGeometry() { for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { sendGeometry((*it).resource); sendDone(*it); } } void OutputInterface::Private::updateScale() { for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { sendScale(*it); sendDone(*it); } } #define SETTER(setterName, type, argumentName) \ void OutputInterface::setterName(type arg) \ { \ Q_D(); \ if (d->argumentName == arg) { \ return; \ } \ d->argumentName = arg; \ emit argumentName##Changed(d->argumentName); \ } SETTER(setPhysicalSize, const QSize&, physicalSize) SETTER(setGlobalPosition, const QPoint&, globalPosition) SETTER(setManufacturer, const QString&, manufacturer) SETTER(setModel, const QString&, model) SETTER(setScale, int, scale) SETTER(setSubPixel, SubPixel, subPixel) SETTER(setTransform, Transform, transform) #undef SETTER QSize OutputInterface::physicalSize() const { Q_D(); return d->physicalSize; } QPoint OutputInterface::globalPosition() const { Q_D(); return d->globalPosition; } QString OutputInterface::manufacturer() const { Q_D(); return d->manufacturer; } QString OutputInterface::model() const { Q_D(); return d->model; } int OutputInterface::scale() const { Q_D(); return d->scale; } OutputInterface::SubPixel OutputInterface::subPixel() const { Q_D(); return d->subPixel; } OutputInterface::Transform OutputInterface::transform() const { Q_D(); return d->transform; } QList< OutputInterface::Mode > OutputInterface::modes() const { Q_D(); return d->modes; } void OutputInterface::setDpmsMode(OutputInterface::DpmsMode mode) { Q_D(); if (d->dpms.mode == mode) { return; } d->dpms.mode = mode; emit dpmsModeChanged(); } void OutputInterface::setDpmsSupported(bool supported) { Q_D(); if (d->dpms.supported == supported) { return; } d->dpms.supported = supported; emit dpmsSupportedChanged(); } OutputInterface::DpmsMode OutputInterface::dpmsMode() const { Q_D(); return d->dpms.mode; } bool OutputInterface::isDpmsSupported() const { Q_D(); return d->dpms.supported; } QVector OutputInterface::clientResources(ClientConnection *client) const { Q_D(); QVector ret; for (auto it = d->resources.constBegin(), end = d->resources.constEnd(); it != end; ++it) { if (wl_resource_get_client((*it).resource) == client->client()) { ret << (*it).resource; } } return ret; } OutputInterface *OutputInterface::get(wl_resource* native) { return Private::get(native); } OutputInterface::Private *OutputInterface::d_func() const { return reinterpret_cast(d.data()); } } } diff --git a/src/server/plasmavirtualdesktop_interface.cpp b/src/server/plasmavirtualdesktop_interface.cpp index 88af8de..8b7f24b 100644 --- a/src/server/plasmavirtualdesktop_interface.cpp +++ b/src/server/plasmavirtualdesktop_interface.cpp @@ -1,402 +1,401 @@ /* SPDX-FileCopyrightText: 2018 Marco Martin SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ #include "plasmavirtualdesktop_interface.h" #include "display.h" #include "global_p.h" #include "resource_p.h" #include #include #include #include namespace KWayland { namespace Server { class Q_DECL_HIDDEN PlasmaVirtualDesktopInterface::Private { public: Private(PlasmaVirtualDesktopInterface *q, PlasmaVirtualDesktopManagementInterface *c); ~Private(); void createResource(wl_resource *parent, quint32 serial); PlasmaVirtualDesktopInterface *q; PlasmaVirtualDesktopManagementInterface *vdm; QVector resources; QString id; QString name; bool active = false; private: static void unbind(wl_resource *resource); static void requestActivateCallback(wl_client *client, wl_resource *resource); static Private *cast(wl_resource *resource) { return reinterpret_cast(wl_resource_get_user_data(resource)); } - wl_listener listener; static const struct org_kde_plasma_virtual_desktop_interface s_interface; }; class Q_DECL_HIDDEN PlasmaVirtualDesktopManagementInterface::Private : public Global::Private { public: Private(PlasmaVirtualDesktopManagementInterface *q, Display *d); QVector resources; QList desktops; quint32 rows = 0; quint32 columns = 0; inline QList::const_iterator constFindDesktop(const QString &id); inline QList::iterator findDesktop(const QString &id); private: 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 getVirtualDesktopCallback(wl_client *client, wl_resource *resource, uint32_t serial, const char *id); static void requestCreateVirtualDesktopCallback(wl_client *client, wl_resource *resource, const char *name, uint32_t position); static void requestRemoveVirtualDesktopCallback(wl_client *client, wl_resource *resource, const char *id); PlasmaVirtualDesktopManagementInterface *q; static const struct org_kde_plasma_virtual_desktop_management_interface s_interface; static const quint32 s_version; }; const quint32 PlasmaVirtualDesktopManagementInterface::Private::s_version = 2; #ifndef K_DOXYGEN const struct org_kde_plasma_virtual_desktop_management_interface PlasmaVirtualDesktopManagementInterface::Private::s_interface = { getVirtualDesktopCallback, requestCreateVirtualDesktopCallback, requestRemoveVirtualDesktopCallback }; #endif inline QList::const_iterator PlasmaVirtualDesktopManagementInterface::Private::constFindDesktop(const QString &id) { return std::find_if( desktops.constBegin(), desktops.constEnd(), [id]( const PlasmaVirtualDesktopInterface *desk ){ return desk->id() == id; } ); } inline QList::iterator PlasmaVirtualDesktopManagementInterface::Private::findDesktop(const QString &id) { return std::find_if( desktops.begin(), desktops.end(), [id]( const PlasmaVirtualDesktopInterface *desk ){ return desk->id() == id; } ); } void PlasmaVirtualDesktopManagementInterface::Private::getVirtualDesktopCallback(wl_client *client, wl_resource *resource, uint32_t serial, const char *id) { Q_UNUSED(client) auto s = cast(resource); auto i = s->constFindDesktop(QString::fromUtf8(id)); if (i == s->desktops.constEnd()) { return; } (*i)->d->createResource(resource, serial); } void PlasmaVirtualDesktopManagementInterface::Private::requestCreateVirtualDesktopCallback(wl_client *client, wl_resource *resource, const char *name, uint32_t position) { Q_UNUSED(client) auto s = cast(resource); emit s->q->desktopCreateRequested(QString::fromUtf8(name), qBound(0, position, (quint32)s->desktops.count())); } void PlasmaVirtualDesktopManagementInterface::Private::requestRemoveVirtualDesktopCallback(wl_client *client, wl_resource *resource, const char *id) { Q_UNUSED(client) auto s = cast(resource); emit s->q->desktopRemoveRequested(QString::fromUtf8(id)); } PlasmaVirtualDesktopManagementInterface::Private::Private(PlasmaVirtualDesktopManagementInterface *q, Display *d) : Global::Private(d, &org_kde_plasma_virtual_desktop_management_interface, s_version) , q(q) { } void PlasmaVirtualDesktopManagementInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&org_kde_plasma_virtual_desktop_management_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } resources << resource; wl_resource_set_implementation(resource, &s_interface, this, unbind); quint32 i = 0; for (auto it = desktops.constBegin(); it != desktops.constEnd(); ++it) { org_kde_plasma_virtual_desktop_management_send_desktop_created(resource, (*it)->id().toUtf8().constData(), i++); } if (wl_resource_get_version(resource) >= ORG_KDE_PLASMA_VIRTUAL_DESKTOP_MANAGEMENT_ROWS_SINCE_VERSION) { org_kde_plasma_virtual_desktop_management_send_rows(resource, rows); } org_kde_plasma_virtual_desktop_management_send_done(resource); } void PlasmaVirtualDesktopManagementInterface::Private::unbind(wl_resource *resource) { auto dm = reinterpret_cast(wl_resource_get_user_data(resource)); dm->resources.removeAll(resource); } PlasmaVirtualDesktopManagementInterface::PlasmaVirtualDesktopManagementInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } PlasmaVirtualDesktopManagementInterface::~PlasmaVirtualDesktopManagementInterface() { Q_D(); qDeleteAll(d->desktops); } PlasmaVirtualDesktopManagementInterface::Private *PlasmaVirtualDesktopManagementInterface::d_func() const { return reinterpret_cast(d.data()); } void PlasmaVirtualDesktopManagementInterface::setRows(quint32 rows) { Q_D(); if (rows == 0 || d->rows == rows) { return; } d->rows = rows; for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { if (wl_resource_get_version(*it) < ORG_KDE_PLASMA_VIRTUAL_DESKTOP_MANAGEMENT_ROWS_SINCE_VERSION) { continue; } org_kde_plasma_virtual_desktop_management_send_rows(*it, rows); } } PlasmaVirtualDesktopInterface *PlasmaVirtualDesktopManagementInterface::desktop(const QString &id) { Q_D(); auto i = d->constFindDesktop(id); if (i != d->desktops.constEnd()) { return *i; } return nullptr; } PlasmaVirtualDesktopInterface *PlasmaVirtualDesktopManagementInterface::createDesktop(const QString &id, quint32 position) { Q_D(); auto i = d->constFindDesktop(id); if (i != d->desktops.constEnd()) { return *i; } const quint32 actualPosition = qMin(position, (quint32)d->desktops.count()); PlasmaVirtualDesktopInterface *desktop = new PlasmaVirtualDesktopInterface(this); desktop->d->id = id; for (auto it = desktop->d->resources.constBegin(); it != desktop->d->resources.constEnd(); ++it) { org_kde_plasma_virtual_desktop_send_desktop_id(*it, id.toUtf8().constData()); } //activate the first desktop TODO: to be done here? if (d->desktops.isEmpty()) { desktop->d->active = true; } d->desktops.insert(actualPosition, desktop); for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { org_kde_plasma_virtual_desktop_management_send_desktop_created(*it, id.toUtf8().constData(), actualPosition); } return desktop; } void PlasmaVirtualDesktopManagementInterface::removeDesktop(const QString &id) { Q_D(); auto deskIt = d->findDesktop(id); if (deskIt == d->desktops.end()) { return; } for (auto it = (*deskIt)->d->resources.constBegin(); it != (*deskIt)->d->resources.constEnd(); ++it) { org_kde_plasma_virtual_desktop_send_removed(*it); } for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { org_kde_plasma_virtual_desktop_management_send_desktop_removed(*it, id.toUtf8().constData()); } (*deskIt)->deleteLater(); d->desktops.erase(deskIt); } QList PlasmaVirtualDesktopManagementInterface::desktops() const { Q_D(); return d->desktops; } void PlasmaVirtualDesktopManagementInterface::sendDone() { Q_D(); for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { org_kde_plasma_virtual_desktop_management_send_done(*it); } } //// PlasmaVirtualDesktopInterface #ifndef K_DOXYGEN const struct org_kde_plasma_virtual_desktop_interface PlasmaVirtualDesktopInterface::Private::s_interface = { requestActivateCallback }; #endif void PlasmaVirtualDesktopInterface::Private::requestActivateCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) auto s = cast(resource); emit s->q->activateRequested(); } PlasmaVirtualDesktopInterface::Private::Private(PlasmaVirtualDesktopInterface *q, PlasmaVirtualDesktopManagementInterface *c) : q(q), vdm(c) { } PlasmaVirtualDesktopInterface::Private::~Private() { // need to copy, as destroy goes through the destroy listener and modifies the list as we iterate const auto c = resources; for (const auto &r : c) { auto client = wl_resource_get_client(r); org_kde_plasma_virtual_desktop_send_removed(r); wl_resource_destroy(r); wl_client_flush(client); } } void PlasmaVirtualDesktopInterface::Private::unbind(wl_resource *resource) { Private *p = reinterpret_cast(wl_resource_get_user_data(resource)); p->resources.removeAll(resource); } void PlasmaVirtualDesktopInterface::Private::createResource(wl_resource *parent, quint32 serial) { ClientConnection *c = vdm->display()->getConnection(wl_resource_get_client(parent)); wl_resource *resource = c->createResource(&org_kde_plasma_virtual_desktop_interface, wl_resource_get_version(parent), serial); if (!resource) { return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); resources << resource; org_kde_plasma_virtual_desktop_send_desktop_id(resource, id.toUtf8().constData()); if (!name.isEmpty()) { org_kde_plasma_virtual_desktop_send_name(resource, name.toUtf8().constData()); } if (active) { org_kde_plasma_virtual_desktop_send_activated(resource); } c->flush(); } PlasmaVirtualDesktopInterface::PlasmaVirtualDesktopInterface(PlasmaVirtualDesktopManagementInterface *parent) : QObject(parent), d(new Private(this, parent)) { } PlasmaVirtualDesktopInterface::~PlasmaVirtualDesktopInterface() { d->vdm->removeDesktop(id()); } QString PlasmaVirtualDesktopInterface::id() const { return d->id; } void PlasmaVirtualDesktopInterface::setName(const QString &name) { if (d->name == name) { return; } d->name = name; for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { org_kde_plasma_virtual_desktop_send_name(*it, name.toUtf8().constData()); } } QString PlasmaVirtualDesktopInterface::name() const { return d->name; } void PlasmaVirtualDesktopInterface::setActive(bool active) { if (d->active == active) { return; } d->active = active; if (active) { for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { org_kde_plasma_virtual_desktop_send_activated(*it); } } else { for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { org_kde_plasma_virtual_desktop_send_deactivated(*it); } } } bool PlasmaVirtualDesktopInterface::isActive() const { return d->active; } void PlasmaVirtualDesktopInterface::sendDone() { for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { org_kde_plasma_virtual_desktop_send_done(*it); } } } } diff --git a/src/server/plasmawindowmanagement_interface.cpp b/src/server/plasmawindowmanagement_interface.cpp index 7a1bc28..0dccda9 100644 --- a/src/server/plasmawindowmanagement_interface.cpp +++ b/src/server/plasmawindowmanagement_interface.cpp @@ -1,963 +1,962 @@ /* SPDX-FileCopyrightText: 2015 Martin Gräßlin SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ #include "plasmawindowmanagement_interface.h" #include "global_p.h" #include "resource_p.h" #include "display.h" #include "surface_interface.h" #include "plasmavirtualdesktop_interface.h" #include #include #include #include #include #include #include #include #include namespace KWayland { namespace Server { class PlasmaWindowManagementInterface::Private : public Global::Private { public: Private(PlasmaWindowManagementInterface *q, Display *d); void sendShowingDesktopState(); ShowingDesktopState state = ShowingDesktopState::Disabled; QVector resources; QList windows; QPointer plasmaVirtualDesktopManagementInterface = nullptr; quint32 windowIdCounter = 0; private: static void unbind(wl_resource *resource); static void showDesktopCallback(wl_client *client, wl_resource *resource, uint32_t state); static void getWindowCallback(wl_client *client, wl_resource *resource, uint32_t id, uint32_t internalWindowId); void bind(wl_client *client, uint32_t version, uint32_t id) override; void sendShowingDesktopState(wl_resource *r); PlasmaWindowManagementInterface *q; static const struct org_kde_plasma_window_management_interface s_interface; static const quint32 s_version; }; class PlasmaWindowInterface::Private { public: Private(PlasmaWindowManagementInterface *wm, PlasmaWindowInterface *q); ~Private(); void createResource(wl_resource *parent, uint32_t id); void setTitle(const QString &title); void setAppId(const QString &appId); void setPid(quint32 pid); void setThemedIconName(const QString &iconName); void setIcon(const QIcon &icon); void setVirtualDesktop(quint32 desktop); void unmap(); void setState(org_kde_plasma_window_management_state flag, bool set); void setParentWindow(PlasmaWindowInterface *parent); void setGeometry(const QRect &geometry); void setApplicationMenuPaths(const QString &service, const QString &object); wl_resource *resourceForParent(PlasmaWindowInterface *parent, wl_resource *child) const; QVector resources; quint32 windowId = 0; QHash minimizedGeometries; PlasmaWindowManagementInterface *wm; bool unmapped = false; PlasmaWindowInterface *parentWindow = nullptr; QMetaObject::Connection parentWindowDestroyConnection; QStringList plasmaVirtualDesktops; QRect geometry; private: static void unbind(wl_resource *resource); static void setStateCallback(wl_client *client, wl_resource *resource, uint32_t flags, uint32_t state); static void setVirtualDesktopCallback(wl_client *client, wl_resource *resource, uint32_t number); static void closeCallback(wl_client *client, wl_resource *resource); static void requestMoveCallback(wl_client *client, wl_resource *resource); static void requestResizeCallback(wl_client *client, wl_resource *resource); static void setMinimizedGeometryCallback(wl_client *client, wl_resource *resource, wl_resource *panel, uint32_t x, uint32_t y, uint32_t width, uint32_t height); static void unsetMinimizedGeometryCallback(wl_client *client, wl_resource *resource, wl_resource *panel); static void destroyCallback(wl_client *client, wl_resource *resource); static void getIconCallback(wl_client *client, wl_resource *resource, int32_t fd); static void requestEnterVirtualDesktopCallback(wl_client *client, wl_resource *resource, const char *id); static void requestEnterNewVirtualDesktopCallback(wl_client *client, wl_resource *resource); static void requestLeaveVirtualDesktopCallback(wl_client *client, wl_resource *resource, const char *id); static Private *cast(wl_resource *resource) { return reinterpret_cast(wl_resource_get_user_data(resource)); } PlasmaWindowInterface *q; QString m_title; QString m_appId; quint32 m_pid = 0; QString m_themedIconName; QString m_appServiceName; QString m_appObjectPath; QIcon m_icon; quint32 m_virtualDesktop = 0; quint32 m_state = 0; - wl_listener listener; static const struct org_kde_plasma_window_interface s_interface; }; const quint32 PlasmaWindowManagementInterface::Private::s_version = 10; PlasmaWindowManagementInterface::Private::Private(PlasmaWindowManagementInterface *q, Display *d) : Global::Private(d, &org_kde_plasma_window_management_interface, s_version) , q(q) { } #ifndef K_DOXYGEN const struct org_kde_plasma_window_management_interface PlasmaWindowManagementInterface::Private::s_interface = { showDesktopCallback, getWindowCallback }; #endif void PlasmaWindowManagementInterface::Private::sendShowingDesktopState() { for (wl_resource *r : resources) { sendShowingDesktopState(r); } } void PlasmaWindowManagementInterface::Private::sendShowingDesktopState(wl_resource *r) { uint32_t s = 0; switch (state) { case ShowingDesktopState::Enabled: s = ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_ENABLED; break; case ShowingDesktopState::Disabled: s = ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_DISABLED; break; default: Q_UNREACHABLE(); break; } org_kde_plasma_window_management_send_show_desktop_changed(r, s); } void PlasmaWindowManagementInterface::Private::showDesktopCallback(wl_client *client, wl_resource *resource, uint32_t state) { Q_UNUSED(client) ShowingDesktopState s = ShowingDesktopState::Disabled; switch (state) { case ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_ENABLED: s = ShowingDesktopState::Enabled; break; case ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_DISABLED: default: s = ShowingDesktopState::Disabled; break; } emit reinterpret_cast(wl_resource_get_user_data(resource))->q->requestChangeShowingDesktop(s); } void PlasmaWindowManagementInterface::Private::getWindowCallback(wl_client *client, wl_resource *resource, uint32_t id, uint32_t internalWindowId) { Q_UNUSED(client) auto p = reinterpret_cast(wl_resource_get_user_data(resource)); auto it = std::find_if(p->windows.constBegin(), p->windows.constEnd(), [internalWindowId] (PlasmaWindowInterface *window) { return window->d->windowId == internalWindowId; } ); if (it == p->windows.constEnd()) { // create a temp window just for the resource and directly send an unmapped PlasmaWindowInterface *window = new PlasmaWindowInterface(p->q, p->q); window->d->unmapped = true; window->d->createResource(resource, id); return; } (*it)->d->createResource(resource, id); } PlasmaWindowManagementInterface::PlasmaWindowManagementInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } PlasmaWindowManagementInterface::~PlasmaWindowManagementInterface() = default; void PlasmaWindowManagementInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *shell = c->createResource(&org_kde_plasma_window_management_interface, qMin(version, s_version), id); if (!shell) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(shell, &s_interface, this, unbind); resources << shell; for (auto it = windows.constBegin(); it != windows.constEnd(); ++it) { org_kde_plasma_window_management_send_window(shell, (*it)->d->windowId); } } void PlasmaWindowManagementInterface::Private::unbind(wl_resource *resource) { auto wm = reinterpret_cast(wl_resource_get_user_data(resource)); wm->resources.removeAll(resource); } void PlasmaWindowManagementInterface::setShowingDesktopState(PlasmaWindowManagementInterface::ShowingDesktopState state) { Q_D(); if (d->state == state) { return; } d->state = state; d->sendShowingDesktopState(); } PlasmaWindowManagementInterface::Private *PlasmaWindowManagementInterface::d_func() const { return reinterpret_cast(d.data()); } PlasmaWindowInterface *PlasmaWindowManagementInterface::createWindow(QObject *parent) { Q_D(); PlasmaWindowInterface *window = new PlasmaWindowInterface(this, parent); // TODO: improve window ids so that it cannot wrap around window->d->windowId = ++d->windowIdCounter; for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { org_kde_plasma_window_management_send_window(*it, window->d->windowId); } d->windows << window; connect(window, &QObject::destroyed, this, [this, window] { Q_D(); d->windows.removeAll(window); } ); return window; } QList PlasmaWindowManagementInterface::windows() const { Q_D(); return d->windows; } void PlasmaWindowManagementInterface::unmapWindow(PlasmaWindowInterface *window) { if (!window) { return; } Q_D(); d->windows.removeOne(window); Q_ASSERT(!d->windows.contains(window)); window->d->unmap(); } void PlasmaWindowManagementInterface::setPlasmaVirtualDesktopManagementInterface(PlasmaVirtualDesktopManagementInterface *manager) { Q_D(); if (d->plasmaVirtualDesktopManagementInterface == manager) { return; } d->plasmaVirtualDesktopManagementInterface = manager; } PlasmaVirtualDesktopManagementInterface *PlasmaWindowManagementInterface::plasmaVirtualDesktopManagementInterface() const { Q_D(); return d->plasmaVirtualDesktopManagementInterface; } #ifndef K_DOXYGEN const struct org_kde_plasma_window_interface PlasmaWindowInterface::Private::s_interface = { setStateCallback, setVirtualDesktopCallback, setMinimizedGeometryCallback, unsetMinimizedGeometryCallback, closeCallback, requestMoveCallback, requestResizeCallback, destroyCallback, getIconCallback, requestEnterVirtualDesktopCallback, requestEnterNewVirtualDesktopCallback, requestLeaveVirtualDesktopCallback }; #endif PlasmaWindowInterface::Private::Private(PlasmaWindowManagementInterface *wm, PlasmaWindowInterface *q) : wm(wm) , q(q) { } PlasmaWindowInterface::Private::~Private() { // need to copy, as destroy goes through the destroy listener and modifies the list as we iterate const auto c = resources; for (const auto &r : c) { auto client = wl_resource_get_client(r); org_kde_plasma_window_send_unmapped(r); wl_resource_destroy(r); wl_client_flush(client); } } void PlasmaWindowInterface::Private::destroyCallback(wl_client *, wl_resource *r) { Private *p = cast(r); p->resources.removeAll(r); wl_resource_destroy(r); if (p->unmapped && p->resources.isEmpty()) { p->q->deleteLater(); } } void PlasmaWindowInterface::Private::unbind(wl_resource *resource) { Private *p = reinterpret_cast(wl_resource_get_user_data(resource)); p->resources.removeAll(resource); if (p->unmapped && p->resources.isEmpty()) { p->q->deleteLater(); } } void PlasmaWindowInterface::Private::createResource(wl_resource *parent, uint32_t id) { ClientConnection *c = wm->display()->getConnection(wl_resource_get_client(parent)); wl_resource *resource = c->createResource(&org_kde_plasma_window_interface, wl_resource_get_version(parent), id); if (!resource) { return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); resources << resource; org_kde_plasma_window_send_virtual_desktop_changed(resource, m_virtualDesktop); for (const auto &desk : plasmaVirtualDesktops) { org_kde_plasma_window_send_virtual_desktop_entered(resource, desk.toUtf8().constData()); } if (!m_appId.isEmpty()) { org_kde_plasma_window_send_app_id_changed(resource, m_appId.toUtf8().constData()); } if (m_pid != 0) { org_kde_plasma_window_send_pid_changed(resource, m_pid); } if (!m_title.isEmpty()) { org_kde_plasma_window_send_title_changed(resource, m_title.toUtf8().constData()); } if (!m_appObjectPath.isEmpty() || !m_appServiceName.isEmpty()) { org_kde_plasma_window_send_application_menu(resource, m_appServiceName.toUtf8().constData(), m_appObjectPath.toUtf8().constData()); } org_kde_plasma_window_send_state_changed(resource, m_state); if (!m_themedIconName.isEmpty()) { org_kde_plasma_window_send_themed_icon_name_changed(resource, m_themedIconName.toUtf8().constData()); } else { if (wl_resource_get_version(resource) >= ORG_KDE_PLASMA_WINDOW_ICON_CHANGED_SINCE_VERSION) { org_kde_plasma_window_send_icon_changed(resource); } } org_kde_plasma_window_send_parent_window(resource, resourceForParent(parentWindow, resource)); if (unmapped) { org_kde_plasma_window_send_unmapped(resource); } if (geometry.isValid() && wl_resource_get_version(resource) >= ORG_KDE_PLASMA_WINDOW_GEOMETRY_SINCE_VERSION) { org_kde_plasma_window_send_geometry(resource, geometry.x(), geometry.y(), geometry.width(), geometry.height()); } if (wl_resource_get_version(resource) >= ORG_KDE_PLASMA_WINDOW_INITIAL_STATE_SINCE_VERSION) { org_kde_plasma_window_send_initial_state(resource); } c->flush(); } void PlasmaWindowInterface::Private::setAppId(const QString &appId) { if (m_appId == appId) { return; } m_appId = appId; const QByteArray utf8 = m_appId.toUtf8(); for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { org_kde_plasma_window_send_app_id_changed(*it, utf8.constData()); } } void PlasmaWindowInterface::Private::setPid(quint32 pid) { if (m_pid == pid) { return; } m_pid = pid; for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { org_kde_plasma_window_send_pid_changed(*it, pid); } } void PlasmaWindowInterface::Private::setThemedIconName(const QString &iconName) { if (m_themedIconName == iconName) { return; } m_themedIconName = iconName; const QByteArray utf8 = m_themedIconName.toUtf8(); for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { org_kde_plasma_window_send_themed_icon_name_changed(*it, utf8.constData()); } } void PlasmaWindowInterface::Private::setIcon(const QIcon &icon) { m_icon = icon; setThemedIconName(m_icon.name()); if (m_icon.name().isEmpty()) { for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { if (wl_resource_get_version(*it) >= ORG_KDE_PLASMA_WINDOW_ICON_CHANGED_SINCE_VERSION) { org_kde_plasma_window_send_icon_changed(*it); } } } } void PlasmaWindowInterface::Private::getIconCallback(wl_client *client, wl_resource *resource, int32_t fd) { Q_UNUSED(client) Private *p = cast(resource); QtConcurrent::run( [fd] (const QIcon &icon) { QFile file; file.open(fd, QIODevice::WriteOnly, QFileDevice::AutoCloseHandle); QDataStream ds(&file); ds << icon; file.close(); }, p->m_icon ); } void PlasmaWindowInterface::Private::requestEnterVirtualDesktopCallback(wl_client *client, wl_resource *resource, const char *id) { Q_UNUSED(client) Private *p = cast(resource); emit p->q->enterPlasmaVirtualDesktopRequested(QString::fromUtf8(id)); } void PlasmaWindowInterface::Private::requestEnterNewVirtualDesktopCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) Private *p = cast(resource); emit p->q->enterNewPlasmaVirtualDesktopRequested(); } void PlasmaWindowInterface::Private::requestLeaveVirtualDesktopCallback(wl_client *client, wl_resource *resource, const char *id) { Q_UNUSED(client) Private *p = cast(resource); emit p->q->leavePlasmaVirtualDesktopRequested(QString::fromUtf8(id)); } void PlasmaWindowInterface::Private::setTitle(const QString &title) { if (m_title == title) { return; } m_title = title; const QByteArray utf8 = m_title.toUtf8(); for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { org_kde_plasma_window_send_title_changed(*it, utf8.constData()); } } void PlasmaWindowInterface::Private::setVirtualDesktop(quint32 desktop) { if (m_virtualDesktop == desktop) { return; } m_virtualDesktop = desktop; for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { org_kde_plasma_window_send_virtual_desktop_changed(*it, m_virtualDesktop); } } void PlasmaWindowInterface::Private::unmap() { unmapped = true; for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { org_kde_plasma_window_send_unmapped(*it); } if (resources.isEmpty()) { q->deleteLater(); } } void PlasmaWindowInterface::Private::setState(org_kde_plasma_window_management_state flag, bool set) { quint32 newState = m_state; if (set) { newState |= flag; } else { newState &= ~flag; } if (newState == m_state) { return; } m_state = newState; for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { org_kde_plasma_window_send_state_changed(*it, m_state); } } wl_resource *PlasmaWindowInterface::Private::resourceForParent(PlasmaWindowInterface *parent, wl_resource *child) const { if (!parent) { return nullptr; } auto it = std::find_if(parent->d->resources.begin(), parent->d->resources.end(), [child] (wl_resource *parentResource) { return wl_resource_get_client(child) == wl_resource_get_client(parentResource); } ); if (it != parent->d->resources.end()) { return *it; } return nullptr; } void PlasmaWindowInterface::Private::setParentWindow(PlasmaWindowInterface *window) { if (parentWindow == window) { return; } QObject::disconnect(parentWindowDestroyConnection); parentWindowDestroyConnection = QMetaObject::Connection(); parentWindow = window; if (parentWindow) { parentWindowDestroyConnection = QObject::connect(window, &QObject::destroyed, q, [this] { parentWindow = nullptr; parentWindowDestroyConnection = QMetaObject::Connection(); for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { org_kde_plasma_window_send_parent_window(*it, nullptr); } } ); } for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { org_kde_plasma_window_send_parent_window(*it, resourceForParent(window, *it)); } } void PlasmaWindowInterface::Private::setGeometry(const QRect &geo) { if (geometry == geo) { return; } geometry = geo; if (!geometry.isValid()) { return; } for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { auto resource = *it; if (wl_resource_get_version(resource) < ORG_KDE_PLASMA_WINDOW_GEOMETRY_SINCE_VERSION) { continue; } org_kde_plasma_window_send_geometry(resource, geometry.x(), geometry.y(), geometry.width(), geometry.height()); } } void PlasmaWindowInterface::Private::setApplicationMenuPaths(const QString &service, const QString &object) { if (m_appServiceName == service && m_appObjectPath == object) { return; } m_appServiceName = service; m_appObjectPath = object; for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { auto resource = *it; if (wl_resource_get_version(resource) < ORG_KDE_PLASMA_WINDOW_APPLICATION_MENU_SINCE_VERSION) { continue; } org_kde_plasma_window_send_application_menu(resource, qUtf8Printable(service), qUtf8Printable(object)); } } void PlasmaWindowInterface::Private::closeCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) Private *p = cast(resource); emit p->q->closeRequested(); } void PlasmaWindowInterface::Private::requestMoveCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) Private *p = cast(resource); emit p->q->moveRequested(); } void PlasmaWindowInterface::Private::requestResizeCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) Private *p = cast(resource); emit p->q->resizeRequested(); } void PlasmaWindowInterface::Private::setVirtualDesktopCallback(wl_client *client, wl_resource *resource, uint32_t number) { Q_UNUSED(client) Private *p = cast(resource); emit p->q->virtualDesktopRequested(number); } void PlasmaWindowInterface::Private::setStateCallback(wl_client *client, wl_resource *resource, uint32_t flags, uint32_t state) { Q_UNUSED(client) Private *p = cast(resource); if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE) { emit p->q->activeRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED) { emit p->q->minimizedRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED) { emit p->q->maximizedRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREEN) { emit p->q->fullscreenRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREEN); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE) { emit p->q->keepAboveRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW) { emit p->q->keepBelowRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_DEMANDS_ATTENTION) { emit p->q->demandsAttentionRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_DEMANDS_ATTENTION); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_CLOSEABLE) { emit p->q->closeableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_CLOSEABLE); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZABLE) { emit p->q->minimizeableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZABLE); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZABLE) { emit p->q->maximizeableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZABLE); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREENABLE) { emit p->q->fullscreenableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREENABLE); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPTASKBAR) { emit p->q->skipTaskbarRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPTASKBAR); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPSWITCHER) { emit p->q->skipSwitcherRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPSWITCHER); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADEABLE) { emit p->q->shadeableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADEABLE); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED) { emit p->q->shadedRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MOVABLE) { emit p->q->movableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MOVABLE); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_RESIZABLE) { emit p->q->resizableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_RESIZABLE); } if (flags & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_VIRTUAL_DESKTOP_CHANGEABLE) { emit p->q->virtualDesktopChangeableRequested(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_VIRTUAL_DESKTOP_CHANGEABLE); } } void PlasmaWindowInterface::Private::setMinimizedGeometryCallback(wl_client *client, wl_resource *resource, wl_resource *panel, uint32_t x, uint32_t y, uint32_t width, uint32_t height) { Q_UNUSED(client) Private *p = cast(resource); SurfaceInterface *panelSurface = SurfaceInterface::get(panel); if (!panelSurface) { return; } if (p->minimizedGeometries.value(panelSurface) == QRect(x, y, width, height)) { return; } p->minimizedGeometries[panelSurface] = QRect(x, y, width, height); emit p->q->minimizedGeometriesChanged(); connect(panelSurface, &QObject::destroyed, p->q, [p, panelSurface] () { if (p->minimizedGeometries.remove(panelSurface)) { emit p->q->minimizedGeometriesChanged(); } }); } void PlasmaWindowInterface::Private::unsetMinimizedGeometryCallback(wl_client *client, wl_resource *resource, wl_resource *panel) { Q_UNUSED(client) Private *p = cast(resource); SurfaceInterface *panelSurface = SurfaceInterface::get(panel); if (!panelSurface) { return; } if (!p->minimizedGeometries.contains(panelSurface)) { return; } p->minimizedGeometries.remove(panelSurface); emit p->q->minimizedGeometriesChanged(); } PlasmaWindowInterface::PlasmaWindowInterface(PlasmaWindowManagementInterface *wm, QObject *parent) : QObject(parent) , d(new Private(wm, this)) { } PlasmaWindowInterface::~PlasmaWindowInterface() = default; void PlasmaWindowInterface::setAppId(const QString &appId) { d->setAppId(appId); } void PlasmaWindowInterface::setPid(quint32 pid) { d->setPid(pid); } void PlasmaWindowInterface::setTitle(const QString &title) { d->setTitle(title); } void PlasmaWindowInterface::setVirtualDesktop(quint32 desktop) { d->setVirtualDesktop(desktop); } void PlasmaWindowInterface::unmap() { d->wm->unmapWindow(this); } QHash PlasmaWindowInterface::minimizedGeometries() const { return d->minimizedGeometries; } void PlasmaWindowInterface::setActive(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE, set); } void PlasmaWindowInterface::setFullscreen(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREEN, set); } void PlasmaWindowInterface::setKeepAbove(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE, set); } void PlasmaWindowInterface::setKeepBelow(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW, set); } void PlasmaWindowInterface::setMaximized(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED, set); } void PlasmaWindowInterface::setMinimized(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED, set); } void PlasmaWindowInterface::setOnAllDesktops(bool set) { //the deprecated vd management d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ON_ALL_DESKTOPS, set); if (!d->wm->plasmaVirtualDesktopManagementInterface()) { return; } //the current vd management if (set) { if (d->plasmaVirtualDesktops.isEmpty()) { return; } //leaving everything means on all desktops for (auto desk : plasmaVirtualDesktops()) { for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { org_kde_plasma_window_send_virtual_desktop_left(*it, desk.toUtf8().constData()); } } d->plasmaVirtualDesktops.clear(); } else { if (!d->plasmaVirtualDesktops.isEmpty()) { return; } //enters the desktops which are active (usually only one but not a given) for (auto desk : d->wm->plasmaVirtualDesktopManagementInterface()->desktops()) { if (desk->isActive() && !d->plasmaVirtualDesktops.contains(desk->id())) { d->plasmaVirtualDesktops << desk->id(); for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { org_kde_plasma_window_send_virtual_desktop_entered(*it, desk->id().toUtf8().constData()); } } } } } void PlasmaWindowInterface::setDemandsAttention(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_DEMANDS_ATTENTION, set); } void PlasmaWindowInterface::setCloseable(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_CLOSEABLE, set); } void PlasmaWindowInterface::setFullscreenable(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREENABLE, set); } void PlasmaWindowInterface::setMaximizeable(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZABLE, set); } void PlasmaWindowInterface::setMinimizeable(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZABLE, set); } void PlasmaWindowInterface::setSkipTaskbar(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPTASKBAR, set); } void PlasmaWindowInterface::setSkipSwitcher(bool skip) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPSWITCHER, skip); } void PlasmaWindowInterface::setThemedIconName(const QString &iconName) { d->setThemedIconName(iconName); } void PlasmaWindowInterface::setIcon(const QIcon &icon) { d->setIcon(icon); } void PlasmaWindowInterface::addPlasmaVirtualDesktop(const QString &id) { //don't add a desktop we're not sure it exists if (!d->wm->plasmaVirtualDesktopManagementInterface() || d->plasmaVirtualDesktops.contains(id)) { return; } PlasmaVirtualDesktopInterface *desktop = d->wm->plasmaVirtualDesktopManagementInterface()->desktop(id); if (!desktop) { return; } d->plasmaVirtualDesktops << id; //if the desktop dies, remove it from or list connect(desktop, &QObject::destroyed, this, [this, id](){removePlasmaVirtualDesktop(id);}); for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { org_kde_plasma_window_send_virtual_desktop_entered(*it, id.toUtf8().constData()); } } void PlasmaWindowInterface::removePlasmaVirtualDesktop(const QString &id) { if (!d->plasmaVirtualDesktops.contains(id)) { return; } d->plasmaVirtualDesktops.removeAll(id); for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { org_kde_plasma_window_send_virtual_desktop_left(*it, id.toUtf8().constData()); } //we went on all desktops if (d->plasmaVirtualDesktops.isEmpty()) { setOnAllDesktops(true); } } QStringList PlasmaWindowInterface::plasmaVirtualDesktops() const { return d->plasmaVirtualDesktops; } void PlasmaWindowInterface::setShadeable(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADEABLE, set); } void PlasmaWindowInterface::setShaded(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED, set); } void PlasmaWindowInterface::setMovable(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MOVABLE, set); } void PlasmaWindowInterface::setResizable(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_RESIZABLE, set); } void PlasmaWindowInterface::setVirtualDesktopChangeable(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_VIRTUAL_DESKTOP_CHANGEABLE, set); } void PlasmaWindowInterface::setParentWindow(PlasmaWindowInterface *parentWindow) { d->setParentWindow(parentWindow); } void PlasmaWindowInterface::setGeometry(const QRect &geometry) { d->setGeometry(geometry); } void PlasmaWindowInterface::setApplicationMenuPaths(const QString &serviceName, const QString &objectPath) { d->setApplicationMenuPaths(serviceName, objectPath); } } } diff --git a/src/server/textinput_interface_v0.cpp b/src/server/textinput_interface_v0.cpp index b1b8f0d..113b6d1 100644 --- a/src/server/textinput_interface_v0.cpp +++ b/src/server/textinput_interface_v0.cpp @@ -1,427 +1,427 @@ /* SPDX-FileCopyrightText: 2016 Martin Gräßlin SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ #include "textinput_interface_p.h" #include "display.h" #include "resource_p.h" #include "seat_interface_p.h" #include "surface_interface.h" #include namespace KWayland { namespace Server { class TextInputUnstableV0Interface::Private : public TextInputInterface::Private { public: Private(TextInputInterface *q, TextInputManagerUnstableV0Interface *c, wl_resource *parentResource); ~Private(); void activate(SeatInterface *seat, SurfaceInterface *surface); void deactivate(); void sendEnter(SurfaceInterface *surface, quint32 serial) override; void sendLeave(quint32 serial, SurfaceInterface *surface) override; void preEdit(const QByteArray &text, const QByteArray &commit) override; void commit(const QByteArray &text) override; void deleteSurroundingText(quint32 beforeLength, quint32 afterLength) override; void setTextDirection(Qt::LayoutDirection direction) override; void setPreEditCursor(qint32 index) override; void setCursorPosition(qint32 index, qint32 anchor) override; void keysymPressed(quint32 keysym, Qt::KeyboardModifiers modifiers) override; void keysymReleased(quint32 keysym, Qt::KeyboardModifiers modifiers) override; TextInputInterfaceVersion interfaceVersion() const override { return TextInputInterfaceVersion::UnstableV0; } void sendInputPanelState() override; void sendLanguage() override; private: static const struct wl_text_input_interface s_interface; TextInputUnstableV0Interface *q_func() { return reinterpret_cast(q); } static void activateCallback(wl_client *client, wl_resource *resource, wl_resource * seat, wl_resource * surface); static void deactivateCallback(wl_client *client, wl_resource *resource, wl_resource * seat); static void resetCallback(wl_client *client, wl_resource *resource); static void setSurroundingTextUintCallback(wl_client *client, wl_resource *resource, const char * text, uint32_t cursor, uint32_t anchor); static void commitStateCallback(wl_client *client, wl_resource *resource, uint32_t serial); static void invokeActionCallback(wl_client *client, wl_resource *resource, uint32_t button, uint32_t index); // helpers TextInputInterface::ContentHints convertContentHint(uint32_t hint) const override; TextInputInterface::ContentPurpose convertContentPurpose(uint32_t purpose) const override; quint32 latestState = 0; }; #ifndef K_DOXYGEN const struct wl_text_input_interface TextInputUnstableV0Interface::Private::s_interface = { activateCallback, deactivateCallback, showInputPanelCallback, hideInputPanelCallback, resetCallback, setSurroundingTextUintCallback, setContentTypeCallback, setCursorRectangleCallback, setPreferredLanguageCallback, commitStateCallback, invokeActionCallback }; #endif void TextInputUnstableV0Interface::Private::activate(SeatInterface *seat, SurfaceInterface *s) { surface = QPointer(s); enabled = true; emit q_func()->enabledChanged(); emit q_func()->requestActivate(seat, surface); } void TextInputUnstableV0Interface::Private::deactivate() { surface.clear(); enabled = false; emit q_func()->enabledChanged(); } void TextInputUnstableV0Interface::Private::sendEnter(SurfaceInterface *surface, quint32 serial) { Q_UNUSED(serial) if (!resource) { return; } wl_text_input_send_enter(resource, surface->resource()); } void TextInputUnstableV0Interface::Private::sendLeave(quint32 serial, SurfaceInterface *surface) { Q_UNUSED(serial) Q_UNUSED(surface) if (!resource) { return; } wl_text_input_send_leave(resource); } void TextInputUnstableV0Interface::Private::preEdit(const QByteArray &text, const QByteArray &commit) { if (!resource) { return; } wl_text_input_send_preedit_string(resource, latestState, text.constData(), commit.constData()); } void TextInputUnstableV0Interface::Private::commit(const QByteArray &text) { if (!resource) { return; } wl_text_input_send_commit_string(resource, latestState, text.constData()); } void TextInputUnstableV0Interface::Private::keysymPressed(quint32 keysym, Qt::KeyboardModifiers modifiers) { Q_UNUSED(modifiers) if (!resource) { return; } wl_text_input_send_keysym(resource, latestState, seat ? seat->timestamp() : 0, keysym, WL_KEYBOARD_KEY_STATE_PRESSED, 0); } void TextInputUnstableV0Interface::Private::keysymReleased(quint32 keysym, Qt::KeyboardModifiers modifiers) { Q_UNUSED(modifiers) if (!resource) { return; } wl_text_input_send_keysym(resource, latestState, seat ? seat->timestamp() : 0, keysym, WL_KEYBOARD_KEY_STATE_RELEASED, 0); } void TextInputUnstableV0Interface::Private::deleteSurroundingText(quint32 beforeLength, quint32 afterLength) { if (!resource) { return; } wl_text_input_send_delete_surrounding_text(resource, -1 * beforeLength, beforeLength + afterLength); } void TextInputUnstableV0Interface::Private::setCursorPosition(qint32 index, qint32 anchor) { if (!resource) { return; } wl_text_input_send_cursor_position(resource, index, anchor); } void TextInputUnstableV0Interface::Private::setTextDirection(Qt::LayoutDirection direction) { if (!resource) { return; } wl_text_input_text_direction wlDirection; switch (direction) { case Qt::LeftToRight: wlDirection = WL_TEXT_INPUT_TEXT_DIRECTION_LTR; break; case Qt::RightToLeft: wlDirection = WL_TEXT_INPUT_TEXT_DIRECTION_RTL; break; case Qt::LayoutDirectionAuto: wlDirection = WL_TEXT_INPUT_TEXT_DIRECTION_AUTO; break; default: Q_UNREACHABLE(); break; } wl_text_input_send_text_direction(resource, latestState, wlDirection); } void TextInputUnstableV0Interface::Private::setPreEditCursor(qint32 index) { if (!resource) { return; } wl_text_input_send_preedit_cursor(resource, index); } void TextInputUnstableV0Interface::Private::sendInputPanelState() { if (!resource) { return; } wl_text_input_send_input_panel_state(resource, inputPanelVisible); } void TextInputUnstableV0Interface::Private::sendLanguage() { if (!resource) { return; } wl_text_input_send_language(resource, latestState, language.constData()); } TextInputUnstableV0Interface::Private::Private(TextInputInterface *q, TextInputManagerUnstableV0Interface *c, wl_resource *parentResource) : TextInputInterface::Private(q, c, parentResource, &wl_text_input_interface, &s_interface) { } TextInputUnstableV0Interface::Private::~Private() = default; void TextInputUnstableV0Interface::Private::activateCallback(wl_client *client, wl_resource *resource, wl_resource *seat, wl_resource *surface) { auto p = cast(resource); Q_ASSERT(*p->client == client); p->activate(SeatInterface::get(seat), SurfaceInterface::get(surface)); } void TextInputUnstableV0Interface::Private::deactivateCallback(wl_client *client, wl_resource *resource, wl_resource *seat) { Q_UNUSED(seat) auto p = cast(resource); Q_ASSERT(*p->client == client); p->deactivate(); } void TextInputUnstableV0Interface::Private::resetCallback(wl_client *client, wl_resource *resource) { auto p = cast(resource); Q_ASSERT(*p->client == client); emit p->q_func()->requestReset(); } void TextInputUnstableV0Interface::Private::setSurroundingTextUintCallback(wl_client *client, wl_resource *resource, const char * text, uint32_t cursor, uint32_t anchor) { setSurroundingTextCallback(client, resource, text, cursor, anchor); } void TextInputUnstableV0Interface::Private::commitStateCallback(wl_client *client, wl_resource *resource, uint32_t serial) { auto p = cast(resource); Q_ASSERT(*p->client == client); p->latestState = serial; } void TextInputUnstableV0Interface::Private::invokeActionCallback(wl_client *client, wl_resource *resource, uint32_t button, uint32_t index) { Q_UNUSED(button) Q_UNUSED(index) // TODO: implement auto p = cast(resource); Q_ASSERT(*p->client == client); } TextInputInterface::ContentHints TextInputUnstableV0Interface::Private::convertContentHint(uint32_t hint) const { const auto hints = wl_text_input_content_hint(hint); TextInputInterface::ContentHints ret = TextInputInterface::ContentHint::None; if (hints & WL_TEXT_INPUT_CONTENT_HINT_AUTO_COMPLETION) { ret |= TextInputInterface::ContentHint::AutoCompletion; } if (hints & WL_TEXT_INPUT_CONTENT_HINT_AUTO_CORRECTION) { ret |= TextInputInterface::ContentHint::AutoCorrection; } if (hints & WL_TEXT_INPUT_CONTENT_HINT_AUTO_CAPITALIZATION) { ret |= TextInputInterface::ContentHint::AutoCapitalization; } if (hints & WL_TEXT_INPUT_CONTENT_HINT_LOWERCASE) { ret |= TextInputInterface::ContentHint::LowerCase; } if (hints & WL_TEXT_INPUT_CONTENT_HINT_UPPERCASE) { ret |= TextInputInterface::ContentHint::UpperCase; } if (hints & WL_TEXT_INPUT_CONTENT_HINT_TITLECASE) { ret |= TextInputInterface::ContentHint::TitleCase; } if (hints & WL_TEXT_INPUT_CONTENT_HINT_HIDDEN_TEXT) { ret |= TextInputInterface::ContentHint::HiddenText; } if (hints & WL_TEXT_INPUT_CONTENT_HINT_SENSITIVE_DATA) { ret |= TextInputInterface::ContentHint::SensitiveData; } if (hints & WL_TEXT_INPUT_CONTENT_HINT_LATIN) { ret |= TextInputInterface::ContentHint::Latin; } if (hints & WL_TEXT_INPUT_CONTENT_HINT_MULTILINE) { ret |= TextInputInterface::ContentHint::MultiLine; } return ret; } TextInputInterface::ContentPurpose TextInputUnstableV0Interface::Private::convertContentPurpose(uint32_t purpose) const { const auto wlPurpose = wl_text_input_content_purpose(purpose); switch (wlPurpose) { case WL_TEXT_INPUT_CONTENT_PURPOSE_ALPHA: return TextInputInterface::ContentPurpose::Alpha; case WL_TEXT_INPUT_CONTENT_PURPOSE_DIGITS: return TextInputInterface::ContentPurpose::Digits; case WL_TEXT_INPUT_CONTENT_PURPOSE_NUMBER: return TextInputInterface::ContentPurpose::Number; case WL_TEXT_INPUT_CONTENT_PURPOSE_PHONE: return TextInputInterface::ContentPurpose::Phone; case WL_TEXT_INPUT_CONTENT_PURPOSE_URL: return TextInputInterface::ContentPurpose::Url; case WL_TEXT_INPUT_CONTENT_PURPOSE_EMAIL: return TextInputInterface::ContentPurpose::Email; case WL_TEXT_INPUT_CONTENT_PURPOSE_NAME: return TextInputInterface::ContentPurpose::Name; case WL_TEXT_INPUT_CONTENT_PURPOSE_PASSWORD: return TextInputInterface::ContentPurpose::Password; case WL_TEXT_INPUT_CONTENT_PURPOSE_DATE: return TextInputInterface::ContentPurpose::Date; case WL_TEXT_INPUT_CONTENT_PURPOSE_TIME: return TextInputInterface::ContentPurpose::Time; case WL_TEXT_INPUT_CONTENT_PURPOSE_DATETIME: return TextInputInterface::ContentPurpose::DateTime; case WL_TEXT_INPUT_CONTENT_PURPOSE_TERMINAL: return TextInputInterface::ContentPurpose::Terminal; case WL_TEXT_INPUT_CONTENT_PURPOSE_NORMAL: default: return TextInputInterface::ContentPurpose::Normal; } } TextInputUnstableV0Interface::TextInputUnstableV0Interface(TextInputManagerUnstableV0Interface *parent, wl_resource *parentResource) : TextInputInterface(new Private(this, parent, parentResource)) { } TextInputUnstableV0Interface::~TextInputUnstableV0Interface() = default; class TextInputManagerUnstableV0Interface::Private : public TextInputManagerInterface::Private { public: Private(TextInputManagerUnstableV0Interface *q, Display *d); private: 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 createTextInputCallback(wl_client *client, wl_resource *resource, uint32_t id); TextInputManagerUnstableV0Interface *q; static const struct wl_text_input_manager_interface s_interface; static const quint32 s_version; }; const quint32 TextInputManagerUnstableV0Interface::Private::s_version = 1; #ifndef K_DOXYGEN const struct wl_text_input_manager_interface TextInputManagerUnstableV0Interface::Private::s_interface = { createTextInputCallback }; #endif void TextInputManagerUnstableV0Interface::Private::createTextInputCallback(wl_client *client, wl_resource *resource, uint32_t id) { auto m = cast(resource); auto *t = new TextInputUnstableV0Interface(m->q, resource); m->inputs << t; QObject::connect(t, &QObject::destroyed, m->q, [t, m] { m->inputs.removeAll(t); } ); QObject::connect(t, &TextInputUnstableV0Interface::requestActivate, m->q, - [t, m] (SeatInterface *seat) { + [t] (SeatInterface *seat) { // TODO: disallow for other seat seat->d_func()->registerTextInput(t); t->d_func()->seat = seat; } ); t->d->create(m->display->getConnection(client), version, id); } TextInputManagerInterface::Private::Private(TextInputInterfaceVersion interfaceVersion, TextInputManagerInterface *q, Display *d, const wl_interface *interface, quint32 version) : Global::Private(d, interface, version) , interfaceVersion(interfaceVersion) , q(q) { } TextInputManagerUnstableV0Interface::Private::Private(TextInputManagerUnstableV0Interface *q, Display *d) : TextInputManagerInterface::Private(TextInputInterfaceVersion::UnstableV0, q, d, &wl_text_input_manager_interface, s_version) , q(q) { } void TextInputManagerUnstableV0Interface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&wl_text_input_manager_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? } void TextInputManagerUnstableV0Interface::Private::unbind(wl_resource *resource) { Q_UNUSED(resource) // TODO: implement? } TextInputManagerUnstableV0Interface::TextInputManagerUnstableV0Interface(Display *display, QObject *parent) : TextInputManagerInterface(new Private(this, display), parent) { } TextInputManagerUnstableV0Interface::~TextInputManagerUnstableV0Interface() = default; } } diff --git a/tests/shadowtest.cpp b/tests/shadowtest.cpp index 54f6873..1e63542 100644 --- a/tests/shadowtest.cpp +++ b/tests/shadowtest.cpp @@ -1,169 +1,169 @@ /* SPDX-FileCopyrightText: 2015 Martin Gräßlin SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ #include "../src/client/compositor.h" #include "../src/client/connection_thread.h" #include "../src/client/event_queue.h" #include "../src/client/registry.h" #include "../src/client/shadow.h" #include "../src/client/shell.h" #include "../src/client/shm_pool.h" #include "../src/client/surface.h" // Qt #include #include #include using namespace KWayland::Client; class ShadowTest : public QObject { Q_OBJECT public: explicit ShadowTest(QObject *parent = nullptr); virtual ~ShadowTest(); void init(); private: void setupRegistry(Registry *registry); void setupShadow(); void render(); QThread *m_connectionThread; ConnectionThread *m_connectionThreadObject; EventQueue *m_eventQueue = nullptr; Compositor *m_compositor = nullptr; Shell *m_shell = nullptr; ShellSurface *m_shellSurface = nullptr; ShmPool *m_shm = nullptr; Surface *m_surface = nullptr; ShadowManager *m_shadowManager = nullptr; }; ShadowTest::ShadowTest(QObject *parent) : QObject(parent) , m_connectionThread(new QThread(this)) , m_connectionThreadObject(new ConnectionThread()) { } ShadowTest::~ShadowTest() { m_connectionThread->quit(); m_connectionThread->wait(); m_connectionThreadObject->deleteLater(); } void ShadowTest::init() { connect(m_connectionThreadObject, &ConnectionThread::connected, this, [this] { m_eventQueue = new EventQueue(this); m_eventQueue->setup(m_connectionThreadObject); Registry *registry = new Registry(this); setupRegistry(registry); }, Qt::QueuedConnection ); m_connectionThreadObject->moveToThread(m_connectionThread); m_connectionThread->start(); m_connectionThreadObject->initConnection(); } void ShadowTest::setupRegistry(Registry *registry) { connect(registry, &Registry::compositorAnnounced, this, [this, registry](quint32 name, quint32 version) { m_compositor = registry->createCompositor(name, version, this); } ); connect(registry, &Registry::shellAnnounced, this, [this, registry](quint32 name, quint32 version) { m_shell = registry->createShell(name, version, this); } ); connect(registry, &Registry::shmAnnounced, this, [this, registry](quint32 name, quint32 version) { m_shm = registry->createShmPool(name, version, this); } ); connect(registry, &Registry::shadowAnnounced, this, [this, registry](quint32 name, quint32 version) { m_shadowManager = registry->createShadowManager(name, version, this); m_shadowManager->setEventQueue(m_eventQueue); } ); connect(registry, &Registry::interfacesAnnounced, this, [this] { Q_ASSERT(m_compositor); Q_ASSERT(m_shell); Q_ASSERT(m_shm); m_surface = m_compositor->createSurface(this); Q_ASSERT(m_surface); setupShadow(); m_shellSurface = m_shell->createSurface(m_surface, this); Q_ASSERT(m_shellSurface); m_shellSurface->setToplevel(); connect(m_shellSurface, &ShellSurface::sizeChanged, this, &ShadowTest::render); render(); } ); registry->setEventQueue(m_eventQueue); registry->create(m_connectionThreadObject); registry->setup(); } void ShadowTest::setupShadow() { Q_ASSERT(m_shadowManager); Shadow *shadow = m_shadowManager->createShadow(m_surface, this); Q_ASSERT(shadow); - auto addElement = [shadow, this](const QColor color) { + auto addElement = [this] (const QColor color) { const QSize size = QSize(10, 10); auto buffer = m_shm->getBuffer(size, size.width() * 4).toStrongRef(); buffer->setUsed(true); QImage image(buffer->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied); image.fill(color); return buffer; }; shadow->attachTopLeft(addElement(Qt::red)); shadow->attachTop(addElement(Qt::darkRed)); shadow->attachTopRight(addElement(Qt::green)); shadow->attachRight(addElement(Qt::darkGreen)); shadow->attachBottomRight(addElement(Qt::darkBlue)); shadow->attachBottom(addElement(Qt::cyan)); shadow->attachBottomLeft(addElement(Qt::darkCyan)); shadow->attachLeft(addElement(Qt::magenta)); shadow->setOffsets(QMarginsF(5, 5, 5, 5)); shadow->commit(); } void ShadowTest::render() { const QSize &size = m_shellSurface->size().isValid() ? m_shellSurface->size() : QSize(300, 200); auto buffer = m_shm->getBuffer(size, size.width() * 4).toStrongRef(); buffer->setUsed(true); QImage image(buffer->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied); image.fill(QColor(255, 255, 255, 128)); m_surface->attachBuffer(*buffer); m_surface->damage(QRect(QPoint(0, 0), size)); m_surface->commit(Surface::CommitFlag::None); buffer->setUsed(false); } int main(int argc, char **argv) { QCoreApplication app(argc, argv); ShadowTest client; client.init(); return app.exec(); } #include "shadowtest.moc" diff --git a/tests/touchclienttest.cpp b/tests/touchclienttest.cpp index df4fe2e..1bbdf97 100644 --- a/tests/touchclienttest.cpp +++ b/tests/touchclienttest.cpp @@ -1,244 +1,244 @@ /* SPDX-FileCopyrightText: 2014, 2015 Martin Gräßlin SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ #include "touchclienttest.h" // KWin::Wayland #include <../src/client/buffer.h> #include <../src/client/compositor.h> #include <../src/client/connection_thread.h> #include <../src/client/event_queue.h> #include <../src/client/keyboard.h> #include <../src/client/output.h> #include <../src/client/pointer.h> #include <../src/client/registry.h> #include <../src/client/seat.h> #include <../src/client/shell.h> #include <../src/client/shm_pool.h> #include <../src/client/surface.h> #include <../src/client/touch.h> // Qt #include #include #include #include #include #include #include #include using namespace KWayland::Client; static Qt::GlobalColor s_colors[] = { Qt::white, Qt::red, Qt::green, Qt::blue, Qt::black }; static int s_colorIndex = 0; WaylandClientTest::WaylandClientTest(QObject *parent) : QObject(parent) , m_connectionThread(new QThread(this)) , m_connectionThreadObject(new ConnectionThread(nullptr)) , m_eventQueue(nullptr) , m_compositor(nullptr) , m_output(nullptr) , m_surface(nullptr) , m_shm(nullptr) , m_timer(new QTimer(this)) { init(); } WaylandClientTest::~WaylandClientTest() { m_connectionThread->quit(); m_connectionThread->wait(); m_connectionThreadObject->deleteLater(); } void WaylandClientTest::init() { connect(m_connectionThreadObject, &ConnectionThread::connected, this, [this]() { // create the event queue for the main gui thread m_eventQueue = new EventQueue(this); m_eventQueue->setup(m_connectionThreadObject); // setup registry Registry *registry = new Registry(this); setupRegistry(registry); }, Qt::QueuedConnection); m_connectionThreadObject->moveToThread(m_connectionThread); m_connectionThread->start(); m_connectionThreadObject->initConnection(); connect(m_timer, &QTimer::timeout, this, [this]() { s_colorIndex = (s_colorIndex + 1) % 5; render(); } ); m_timer->setInterval(1000); m_timer->start(); } void WaylandClientTest::setupRegistry(Registry *registry) { connect(registry, &Registry::compositorAnnounced, this, [this, registry](quint32 name) { m_compositor = registry->createCompositor(name, 1, this); m_surface = m_compositor->createSurface(this); } ); connect(registry, &Registry::shellAnnounced, this, [this, registry](quint32 name) { Shell *shell = registry->createShell(name, 1, this); ShellSurface *shellSurface = shell->createSurface(m_surface, m_surface); shellSurface->setToplevel(); render(QSize(400, 200)); } ); connect(registry, &Registry::outputAnnounced, this, [this, registry](quint32 name) { if (m_output) { return; } m_output = registry->createOutput(name, 2, this); } ); connect(registry, &Registry::shmAnnounced, this, [this, registry](quint32 name) { m_shm = registry->createShmPool(name, 1, this); } ); connect(registry, &Registry::seatAnnounced, this, [this, registry](quint32 name) { Seat *s = registry->createSeat(name, 2, this); connect(s, &Seat::hasKeyboardChanged, this, [this, s](bool has) { if (!has) { return; } Keyboard *k = s->createKeyboard(this); connect(k, &Keyboard::keyChanged, this, - [this](quint32 key, Keyboard::KeyState state) { + [] (quint32 key, Keyboard::KeyState state) { if (key == KEY_Q && state == Keyboard::KeyState::Released) { QCoreApplication::instance()->quit(); } } ); } ); connect(s, &Seat::hasPointerChanged, this, [this, s](bool has) { if (!has) { return; } Pointer *p = s->createPointer(this); connect(p, &Pointer::buttonStateChanged, this, [this](quint32 serial, quint32 time, quint32 button, Pointer::ButtonState state) { Q_UNUSED(serial) Q_UNUSED(time) if (state == Pointer::ButtonState::Released) { if (button == BTN_LEFT) { if (m_timer->isActive()) { m_timer->stop(); } else { m_timer->start(); } } if (button == BTN_RIGHT) { QCoreApplication::instance()->quit(); } } } ); } ); connect(s, &Seat::hasTouchChanged, this, [this, s](bool has) { if (!has) { return; } Touch *t = s->createTouch(this); connect(t, &Touch::sequenceStarted, this, [] (KWayland::Client::TouchPoint *startPoint) { qDebug() << "Touch sequence started at" << startPoint->position() << "with id" << startPoint->id(); } ); connect(t, &Touch::sequenceCanceled, this, [] () { qDebug() << "Touch sequence canceled"; } ); connect(t, &Touch::sequenceEnded, this, [] () { qDebug() << "Touch sequence finished"; } ); connect(t, &Touch::frameEnded, this, [] () { qDebug() << "End of touch contact point list"; } ); connect(t, &Touch::pointAdded, this, [] (KWayland::Client::TouchPoint *point) { qDebug() << "Touch point added at" << point->position() << "with id" << point->id(); } ); connect(t, &Touch::pointRemoved, this, [] (KWayland::Client::TouchPoint *point) { qDebug() << "Touch point " << point->id() << " removed at" << point->position(); } ); connect(t, &Touch::pointMoved, this, [] (KWayland::Client::TouchPoint *point) { qDebug() << "Touch point " << point->id() << " moved to" << point->position(); } ); } ); } ); registry->create(m_connectionThreadObject->display()); registry->setEventQueue(m_eventQueue); registry->setup(); } void WaylandClientTest::render(const QSize &size) { m_currentSize = size; render(); } void WaylandClientTest::render() { if (!m_shm || !m_surface || !m_surface->isValid() || !m_currentSize.isValid()) { return; } auto buffer = m_shm->getBuffer(m_currentSize, m_currentSize.width() * 4).toStrongRef(); buffer->setUsed(true); QImage image(buffer->address(), m_currentSize.width(), m_currentSize.height(), QImage::Format_ARGB32_Premultiplied); image.fill(s_colors[s_colorIndex]); m_surface->attachBuffer(*buffer); m_surface->damage(QRect(QPoint(0, 0), m_currentSize)); m_surface->commit(Surface::CommitFlag::None); buffer->setUsed(false); } int main(int argc, char **argv) { QCoreApplication app(argc, argv); new WaylandClientTest(&app); return app.exec(); } diff --git a/tests/xdgforeigntest.cpp b/tests/xdgforeigntest.cpp index 22a4309..9e083dc 100644 --- a/tests/xdgforeigntest.cpp +++ b/tests/xdgforeigntest.cpp @@ -1,194 +1,196 @@ /* SPDX-FileCopyrightText: 2016 Martin Gräßlin SPDX-FileCopyrightText: 2017 Marco Martin SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ #include "../src/client/compositor.h" #include "../src/client/connection_thread.h" #include "../src/client/event_queue.h" #include "../src/client/registry.h" #include "../src/client/shell.h" #include "../src/client/shm_pool.h" #include "../src/client/server_decoration.h" #include "../src/client/xdgshell.h" #include "../src/client/xdgforeign.h" // Qt #include #include #include #include #include using namespace KWayland::Client; class XdgForeignTest : public QObject { Q_OBJECT public: explicit XdgForeignTest(QObject *parent = nullptr); virtual ~XdgForeignTest(); void init(); private: void setupRegistry(Registry *registry); void render(); QThread *m_connectionThread; ConnectionThread *m_connectionThreadObject; EventQueue *m_eventQueue = nullptr; Compositor *m_compositor = nullptr; XdgShell *m_shell = nullptr; XdgShellSurface *m_shellSurface = nullptr; ShmPool *m_shm = nullptr; Surface *m_surface = nullptr; XdgShellSurface *m_childShellSurface = nullptr; Surface *m_childSurface = nullptr; KWayland::Client::XdgExporter *m_exporter = nullptr; KWayland::Client::XdgImporter *m_importer = nullptr; KWayland::Client::XdgExported *m_exported = nullptr; KWayland::Client::XdgImported *m_imported = nullptr; KWayland::Client::ServerSideDecorationManager *m_decoration = nullptr; }; XdgForeignTest::XdgForeignTest(QObject *parent) : QObject(parent) , m_connectionThread(new QThread(this)) , m_connectionThreadObject(new ConnectionThread()) { } XdgForeignTest::~XdgForeignTest() { m_connectionThread->quit(); m_connectionThread->wait(); m_connectionThreadObject->deleteLater(); } void XdgForeignTest::init() { connect(m_connectionThreadObject, &ConnectionThread::connected, this, [this] { m_eventQueue = new EventQueue(this); m_eventQueue->setup(m_connectionThreadObject); Registry *registry = new Registry(this); setupRegistry(registry); }, Qt::QueuedConnection ); m_connectionThreadObject->moveToThread(m_connectionThread); m_connectionThread->start(); m_connectionThreadObject->initConnection(); } void XdgForeignTest::setupRegistry(Registry *registry) { connect(registry, &Registry::compositorAnnounced, this, [this, registry](quint32 name, quint32 version) { m_compositor = registry->createCompositor(name, version, this); } ); connect(registry, &Registry::xdgShellUnstableV5Announced, this, [this, registry](quint32 name, quint32 version) { m_shell = registry->createXdgShell(name, version, this); } ); connect(registry, &Registry::shmAnnounced, this, [this, registry](quint32 name, quint32 version) { m_shm = registry->createShmPool(name, version, this); } ); connect(registry, &Registry::exporterUnstableV2Announced, this, [this, registry](quint32 name, quint32 version) { m_exporter = registry->createXdgExporter(name, version, this); m_exporter->setEventQueue(m_eventQueue); } ); connect(registry, &Registry::importerUnstableV2Announced, this, [this, registry](quint32 name, quint32 version) { m_importer = registry->createXdgImporter(name, version, this); m_importer->setEventQueue(m_eventQueue); } ); connect(registry, &Registry::serverSideDecorationManagerAnnounced, this, [this, registry](quint32 name, quint32 version) { m_decoration = registry->createServerSideDecorationManager(name, version, this); m_decoration->setEventQueue(m_eventQueue); } ); connect(registry, &Registry::interfacesAnnounced, this, [this] { Q_ASSERT(m_compositor); Q_ASSERT(m_shell); Q_ASSERT(m_shm); Q_ASSERT(m_exporter); Q_ASSERT(m_importer); m_surface = m_compositor->createSurface(this); Q_ASSERT(m_surface); auto parentDeco = m_decoration->create(m_surface, this); + Q_UNUSED(parentDeco) m_shellSurface = m_shell->createSurface(m_surface, this); Q_ASSERT(m_shellSurface); connect(m_shellSurface, &XdgShellSurface::sizeChanged, this, &XdgForeignTest::render); m_childSurface = m_compositor->createSurface(this); Q_ASSERT(m_childSurface); auto childDeco = m_decoration->create(m_childSurface, this); + Q_UNUSED(childDeco) m_childShellSurface = m_shell->createSurface(m_childSurface, this); Q_ASSERT(m_childShellSurface); connect(m_childShellSurface, &XdgShellSurface::sizeChanged, this, &XdgForeignTest::render); m_exported = m_exporter->exportTopLevel(m_surface, this); Q_ASSERT(m_decoration); connect(m_exported, &XdgExported::done, this, [this]() { m_imported = m_importer->importTopLevel(m_exported->handle(), this); m_imported->setParentOf(m_childSurface); }); render(); } ); registry->setEventQueue(m_eventQueue); registry->create(m_connectionThreadObject); registry->setup(); } void XdgForeignTest::render() { QSize size = m_shellSurface->size().isValid() ? m_shellSurface->size() : QSize(500, 500); auto buffer = m_shm->getBuffer(size, size.width() * 4).toStrongRef(); buffer->setUsed(true); QImage image(buffer->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied); image.fill(QColor(255, 255, 255, 255)); m_surface->attachBuffer(*buffer); m_surface->damage(QRect(QPoint(0, 0), size)); m_surface->commit(Surface::CommitFlag::None); buffer->setUsed(false); size = m_childShellSurface->size().isValid() ? m_childShellSurface->size() : QSize(200, 200); buffer = m_shm->getBuffer(size, size.width() * 4).toStrongRef(); buffer->setUsed(true); image = QImage(buffer->address(), size.width(), size.height(), QImage::Format_ARGB32_Premultiplied); image.fill(QColor(255, 0, 0, 255)); m_childSurface->attachBuffer(*buffer); m_childSurface->damage(QRect(QPoint(0, 0), size)); m_childSurface->commit(Surface::CommitFlag::None); buffer->setUsed(false); } int main(int argc, char **argv) { QCoreApplication app(argc, argv); XdgForeignTest client; client.init(); return app.exec(); } #include "xdgforeigntest.moc"