diff --git a/autotests/client/test_plasma_virtual_desktop.cpp b/autotests/client/test_plasma_virtual_desktop.cpp index 5ef4093..8ebc6a1 100644 --- a/autotests/client/test_plasma_virtual_desktop.cpp +++ b/autotests/client/test_plasma_virtual_desktop.cpp @@ -1,548 +1,534 @@ /******************************************************************** Copyright 2018 Marco Martin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ // Qt #include // KWin #include "../../src/client/compositor.h" #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/region.h" #include "../../src/client/registry.h" #include "../../src/client/surface.h" #include "../../src/client/plasmavirtualdesktop.h" #include "../../src/server/display.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/region_interface.h" #include "../../src/server/plasmavirtualdesktop_interface.h" #include "../../src/server/plasmawindowmanagement_interface.h" #include "../../src/client/plasmawindowmanagement.h" using namespace KWayland::Client; class TestVirtualDesktop : public QObject { Q_OBJECT public: explicit TestVirtualDesktop(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testCreate(); void testConnectNewClient(); void testDestroy(); void testActivate(); void testEnterLeaveDesktop(); void testAllDesktops(); void testCreateRequested(); void testRemoveRequested(); private: KWayland::Server::Display *m_display; KWayland::Server::CompositorInterface *m_compositorInterface; KWayland::Server::PlasmaVirtualDesktopManagementInterface *m_plasmaVirtualDesktopManagementInterface; KWayland::Server::PlasmaWindowManagementInterface *m_windowManagementInterface; KWayland::Server::PlasmaWindowInterface *m_windowInterface; KWayland::Client::ConnectionThread *m_connection; KWayland::Client::Compositor *m_compositor; KWayland::Client::PlasmaVirtualDesktopManagement *m_plasmaVirtualDesktopManagement; KWayland::Client::EventQueue *m_queue; KWayland::Client::PlasmaWindowManagement *m_windowManagement; KWayland::Client::PlasmaWindow *m_window; QThread *m_thread; }; static const QString s_socketName = QStringLiteral("kwayland-test-wayland-virtual-desktop-0"); TestVirtualDesktop::TestVirtualDesktop(QObject *parent) : QObject(parent) , m_display(nullptr) , m_compositorInterface(nullptr) , m_connection(nullptr) , m_compositor(nullptr) , m_queue(nullptr) , m_thread(nullptr) { } void TestVirtualDesktop::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, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new KWayland::Client::EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); Registry registry; QSignalSpy compositorSpy(®istry, &Registry::compositorAnnounced); QVERIFY(compositorSpy.isValid()); QSignalSpy plasmaVirtualDesktopManagementSpy(®istry, &Registry::plasmaVirtualDesktopManagementAnnounced); QVERIFY(plasmaVirtualDesktopManagementSpy.isValid()); QSignalSpy windowManagementSpy(®istry, SIGNAL(plasmaWindowManagementAnnounced(quint32,quint32))); QVERIFY(windowManagementSpy.isValid()); QVERIFY(!registry.eventQueue()); registry.setEventQueue(m_queue); QCOMPARE(registry.eventQueue(), m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); m_compositorInterface = m_display->createCompositor(m_display); m_compositorInterface->create(); QVERIFY(m_compositorInterface->isValid()); QVERIFY(compositorSpy.wait()); m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); m_plasmaVirtualDesktopManagementInterface = m_display->createPlasmaVirtualDesktopManagement(m_display); m_plasmaVirtualDesktopManagementInterface->create(); QVERIFY(m_plasmaVirtualDesktopManagementInterface->isValid()); QVERIFY(plasmaVirtualDesktopManagementSpy.wait()); m_plasmaVirtualDesktopManagement = registry.createPlasmaVirtualDesktopManagement(plasmaVirtualDesktopManagementSpy.first().first().value(), plasmaVirtualDesktopManagementSpy.first().last().value(), this); m_windowManagementInterface = m_display->createPlasmaWindowManagement(m_display); m_windowManagementInterface->create(); QVERIFY(m_windowManagementInterface->isValid()); m_windowManagementInterface->setPlasmaVirtualDesktopManagementInterface(m_plasmaVirtualDesktopManagementInterface); QVERIFY(windowManagementSpy.wait()); m_windowManagement = registry.createPlasmaWindowManagement(windowManagementSpy.first().first().value(), windowManagementSpy.first().last().value(), this); QSignalSpy windowSpy(m_windowManagement, SIGNAL(windowCreated(KWayland::Client::PlasmaWindow*))); QVERIFY(windowSpy.isValid()); m_windowInterface = m_windowManagementInterface->createWindow(this); m_windowInterface->setPid(1337); QVERIFY(windowSpy.wait()); m_window = windowSpy.first().first().value(); } void TestVirtualDesktop::cleanup() { #define CLEANUP(variable) \ if (variable) { \ delete variable; \ variable = nullptr; \ } CLEANUP(m_compositor) CLEANUP(m_plasmaVirtualDesktopManagement) CLEANUP(m_windowInterface) CLEANUP(m_windowManagement) CLEANUP(m_queue) if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } CLEANUP(m_compositorInterface) CLEANUP(m_plasmaVirtualDesktopManagementInterface) CLEANUP(m_windowManagementInterface) CLEANUP(m_display) #undef CLEANUP } void TestVirtualDesktop::testCreate() { QSignalSpy desktopCreatedSpy(m_plasmaVirtualDesktopManagement, &PlasmaVirtualDesktopManagement::desktopCreated); QSignalSpy managementDoneSpy(m_plasmaVirtualDesktopManagement, &PlasmaVirtualDesktopManagement::done); //on this createDesktop bind() isn't called already, the desktopadded signals will be sent after bind happened KWayland::Server::PlasmaVirtualDesktopInterface *desktop1Int = m_plasmaVirtualDesktopManagementInterface->createDesktop(QStringLiteral("0-1")); desktop1Int->setName("Desktop 1"); desktopCreatedSpy.wait(); QList arguments = desktopCreatedSpy.takeFirst(); QCOMPARE(arguments.at(0).toString(), QStringLiteral("0-1")); QCOMPARE(arguments.at(1).toUInt(), (quint32)0); m_plasmaVirtualDesktopManagementInterface->sendDone(); managementDoneSpy.wait(); QCOMPARE(m_plasmaVirtualDesktopManagement->desktops().length(), 1); KWayland::Client::PlasmaVirtualDesktop *desktop1 = m_plasmaVirtualDesktopManagement->desktops().first(); QSignalSpy desktop1DoneSpy(desktop1, &PlasmaVirtualDesktop::done); desktop1Int->sendDone(); desktop1DoneSpy.wait(); QCOMPARE(desktop1->id(), QStringLiteral("0-1")); QCOMPARE(desktop1->name(), QStringLiteral("Desktop 1")); //on those createDesktop the bind will already be done KWayland::Server::PlasmaVirtualDesktopInterface *desktop2Int = m_plasmaVirtualDesktopManagementInterface->createDesktop(QStringLiteral("0-2")); desktop2Int->setName("Desktop 2"); desktopCreatedSpy.wait(); arguments = desktopCreatedSpy.takeFirst(); QCOMPARE(arguments.at(0).toString(), QStringLiteral("0-2")); QCOMPARE(arguments.at(1).toUInt(), (quint32)1); QCOMPARE(m_plasmaVirtualDesktopManagement->desktops().length(), 2); KWayland::Server::PlasmaVirtualDesktopInterface *desktop3Int = m_plasmaVirtualDesktopManagementInterface->createDesktop(QStringLiteral("0-3")); desktop3Int->setName("Desktop 3"); desktopCreatedSpy.wait(); arguments = desktopCreatedSpy.takeFirst(); QCOMPARE(arguments.at(0).toString(), QStringLiteral("0-3")); QCOMPARE(m_plasmaVirtualDesktopManagement->desktops().length(), 3); m_plasmaVirtualDesktopManagementInterface->sendDone(); managementDoneSpy.wait(); //get the clients KWayland::Client::PlasmaVirtualDesktop *desktop2 = m_plasmaVirtualDesktopManagement->desktops()[1]; QSignalSpy desktop2DoneSpy(desktop2, &PlasmaVirtualDesktop::done); desktop2Int->sendDone(); desktop2DoneSpy.wait(); KWayland::Client::PlasmaVirtualDesktop *desktop3 = m_plasmaVirtualDesktopManagement->desktops()[2]; QSignalSpy desktop3DoneSpy(desktop3, &PlasmaVirtualDesktop::done); desktop3Int->sendDone(); desktop3DoneSpy.wait(); QCOMPARE(desktop1->id(), QStringLiteral("0-1")); QCOMPARE(desktop1->name(), QStringLiteral("Desktop 1")); QCOMPARE(desktop2->id(), QStringLiteral("0-2")); QCOMPARE(desktop2->name(), QStringLiteral("Desktop 2")); QCOMPARE(desktop3->id(), QStringLiteral("0-3")); QCOMPARE(desktop3->name(), QStringLiteral("Desktop 3")); //coherence of order between client and server QCOMPARE(m_plasmaVirtualDesktopManagementInterface->desktops().length(), 3); QCOMPARE(m_plasmaVirtualDesktopManagement->desktops().length(), 3); for (int i = 0; i < m_plasmaVirtualDesktopManagement->desktops().length(); ++i) { QCOMPARE(m_plasmaVirtualDesktopManagementInterface->desktops().at(i)->id(), m_plasmaVirtualDesktopManagement->desktops().at(i)->id()); } } void TestVirtualDesktop::testConnectNewClient() { //rebuild some desktops testCreate(); Registry registry; QVERIFY(!registry.eventQueue()); registry.setEventQueue(m_queue); QCOMPARE(registry.eventQueue(), m_queue); registry.create(m_connection->display()); QVERIFY(registry.isValid()); registry.setup(); QSignalSpy plasmaVirtualDesktopManagementSpy(®istry, &Registry::plasmaVirtualDesktopManagementAnnounced); QVERIFY(plasmaVirtualDesktopManagementSpy.isValid()); QVERIFY(plasmaVirtualDesktopManagementSpy.wait()); KWayland::Client::PlasmaVirtualDesktopManagement *otherPlasmaVirtualDesktopManagement = registry.createPlasmaVirtualDesktopManagement(plasmaVirtualDesktopManagementSpy.first().first().value(), plasmaVirtualDesktopManagementSpy.first().last().value(), this); QSignalSpy managementDoneSpy(otherPlasmaVirtualDesktopManagement, &PlasmaVirtualDesktopManagement::done); managementDoneSpy.wait(); QCOMPARE(otherPlasmaVirtualDesktopManagement->desktops().length(), 3); delete otherPlasmaVirtualDesktopManagement; } void TestVirtualDesktop::testDestroy() { //rebuild some desktops testCreate(); KWayland::Server::PlasmaVirtualDesktopInterface *desktop1Int = m_plasmaVirtualDesktopManagementInterface->desktops().first(); KWayland::Client::PlasmaVirtualDesktop *desktop1 = m_plasmaVirtualDesktopManagement->desktops().first(); QSignalSpy desktop1IntDestroyedSpy(desktop1Int, &QObject::destroyed); QSignalSpy desktop1DestroyedSpy(desktop1, &QObject::destroyed); QSignalSpy desktop1RemovedSpy(desktop1, &KWayland::Client::PlasmaVirtualDesktop::removed); m_plasmaVirtualDesktopManagementInterface->removeDesktop(QStringLiteral("0-1")); //test that both server and client desktoip interfaces go away desktop1IntDestroyedSpy.wait(); desktop1RemovedSpy.wait(); desktop1DestroyedSpy.wait(); //coherence of order between client and server QCOMPARE(m_plasmaVirtualDesktopManagementInterface->desktops().length(), 2); QCOMPARE(m_plasmaVirtualDesktopManagement->desktops().length(), 2); for (int i = 0; i < m_plasmaVirtualDesktopManagement->desktops().length(); ++i) { QCOMPARE(m_plasmaVirtualDesktopManagementInterface->desktops().at(i)->id(), m_plasmaVirtualDesktopManagement->desktops().at(i)->id()); } //Test the desktopRemoved signal of the manager, remove another desktop as the signals can't be tested at the same time QSignalSpy desktopManagerRemovedSpy(m_plasmaVirtualDesktopManagement, &KWayland::Client::PlasmaVirtualDesktopManagement::desktopRemoved); m_plasmaVirtualDesktopManagementInterface->removeDesktop(QStringLiteral("0-2")); desktopManagerRemovedSpy.wait(); QCOMPARE(desktopManagerRemovedSpy.takeFirst().at(0).toString(), QStringLiteral("0-2")); QCOMPARE(m_plasmaVirtualDesktopManagementInterface->desktops().length(), 1); QCOMPARE(m_plasmaVirtualDesktopManagement->desktops().length(), 1); } void TestVirtualDesktop::testActivate() { //rebuild some desktops testCreate(); KWayland::Server::PlasmaVirtualDesktopInterface *desktop1Int = m_plasmaVirtualDesktopManagementInterface->desktops().first(); KWayland::Client::PlasmaVirtualDesktop *desktop1 = m_plasmaVirtualDesktopManagement->desktops().first(); QVERIFY(desktop1->isActive()); QVERIFY(desktop1Int->isActive()); KWayland::Server::PlasmaVirtualDesktopInterface *desktop2Int = m_plasmaVirtualDesktopManagementInterface->desktops()[1]; KWayland::Client::PlasmaVirtualDesktop *desktop2 = m_plasmaVirtualDesktopManagement->desktops()[1]; QVERIFY(!desktop2Int->isActive()); QSignalSpy requestActivateSpy(desktop2Int, &KWayland::Server::PlasmaVirtualDesktopInterface::activateRequested); QSignalSpy activatedSpy(desktop2, &KWayland::Client::PlasmaVirtualDesktop::activated); desktop2->requestActivate(); requestActivateSpy.wait(); //This simulates a compositor which supports only one active desktop at a time for (auto *deskInt : m_plasmaVirtualDesktopManagementInterface->desktops()) { if (deskInt->id() == desktop2->id()) { deskInt->setActive(true); } else { deskInt->setActive(false); } } activatedSpy.wait(); //correct state in the server QVERIFY(desktop2Int->isActive()); QVERIFY(!desktop1Int->isActive()); //correct state in the client QVERIFY(desktop2Int->isActive()); QVERIFY(!desktop1Int->isActive()); //Test the deactivated signal QSignalSpy deactivatedSpy(desktop2, &KWayland::Client::PlasmaVirtualDesktop::deactivated); for (auto *deskInt : m_plasmaVirtualDesktopManagementInterface->desktops()) { if (deskInt->id() == desktop1->id()) { deskInt->setActive(true); } else { deskInt->setActive(false); } } deactivatedSpy.wait(); } void TestVirtualDesktop::testEnterLeaveDesktop() { testCreate(); QSignalSpy enterRequestedSpy(m_windowInterface, &KWayland::Server::PlasmaWindowInterface::enterPlasmaVirtualDesktopRequested); m_window->requestEnterVirtualDesktop(QStringLiteral("0-1")); enterRequestedSpy.wait(); QCOMPARE(enterRequestedSpy.takeFirst().at(0).toString(), QStringLiteral("0-1")); QSignalSpy virtualDesktopEnteredSpy(m_window, &KWayland::Client::PlasmaWindow::plasmaVirtualDesktopEntered); //agree to the request m_windowInterface->addPlasmaVirtualDesktop(QStringLiteral("0-1")); QCOMPARE(m_windowInterface->plasmaVirtualDesktops().length(), 1); QCOMPARE(m_windowInterface->plasmaVirtualDesktops().first(), QStringLiteral("0-1")); //check if the client received the enter virtualDesktopEnteredSpy.wait(); QCOMPARE(virtualDesktopEnteredSpy.takeFirst().at(0).toString(), QStringLiteral("0-1")); QCOMPARE(m_window->plasmaVirtualDesktops().length(), 1); QCOMPARE(m_window->plasmaVirtualDesktops().first(), QStringLiteral("0-1")); //add another desktop, server side m_windowInterface->addPlasmaVirtualDesktop(QStringLiteral("0-3")); virtualDesktopEnteredSpy.wait(); QCOMPARE(virtualDesktopEnteredSpy.takeFirst().at(0).toString(), QStringLiteral("0-3")); QCOMPARE(m_windowInterface->plasmaVirtualDesktops().length(), 2); QCOMPARE(m_window->plasmaVirtualDesktops().length(), 2); QCOMPARE(m_window->plasmaVirtualDesktops()[1], QStringLiteral("0-3")); //try to add an invalid desktop m_windowInterface->addPlasmaVirtualDesktop(QStringLiteral("invalid")); QCOMPARE(m_window->plasmaVirtualDesktops().length(), 2); //remove a desktop QSignalSpy leaveRequestedSpy(m_windowInterface, &KWayland::Server::PlasmaWindowInterface::leavePlasmaVirtualDesktopRequested); m_window->requestLeaveVirtualDesktop(QStringLiteral("0-1")); leaveRequestedSpy.wait(); QCOMPARE(leaveRequestedSpy.takeFirst().at(0).toString(), QStringLiteral("0-1")); QSignalSpy virtualDesktopLeftSpy(m_window, &KWayland::Client::PlasmaWindow::plasmaVirtualDesktopLeft); //agree to the request m_windowInterface->removePlasmaVirtualDesktop(QStringLiteral("0-1")); QCOMPARE(m_windowInterface->plasmaVirtualDesktops().length(), 1); QCOMPARE(m_windowInterface->plasmaVirtualDesktops().first(), QStringLiteral("0-3")); //check if the client received the leave virtualDesktopLeftSpy.wait(); QCOMPARE(virtualDesktopLeftSpy.takeFirst().at(0).toString(), QStringLiteral("0-1")); QCOMPARE(m_window->plasmaVirtualDesktops().length(), 1); QCOMPARE(m_window->plasmaVirtualDesktops().first(), QStringLiteral("0-3")); //Destroy desktop 2 m_plasmaVirtualDesktopManagementInterface->removeDesktop(QStringLiteral("0-3")); //the window should receive a left signal from the destroyed desktop virtualDesktopLeftSpy.wait(); QCOMPARE(m_window->plasmaVirtualDesktops().length(), 0); } void TestVirtualDesktop::testAllDesktops() { testCreate(); QSignalSpy virtualDesktopEnteredSpy(m_window, &KWayland::Client::PlasmaWindow::plasmaVirtualDesktopEntered); QSignalSpy virtualDesktopLeftSpy(m_window, &KWayland::Client::PlasmaWindow::plasmaVirtualDesktopLeft); //in the beginning the window is on desktop 1 and desktop 3 m_windowInterface->addPlasmaVirtualDesktop(QStringLiteral("0-1")); m_windowInterface->addPlasmaVirtualDesktop(QStringLiteral("0-3")); virtualDesktopEnteredSpy.wait(); //setting on all desktops QCOMPARE(m_window->plasmaVirtualDesktops().length(), 2); m_windowInterface->setOnAllDesktops(true); //setting on all desktops, the window will leave every desktop virtualDesktopLeftSpy.wait(); QCOMPARE(virtualDesktopLeftSpy.count(), 2); + QCOMPARE(m_window->plasmaVirtualDesktops().length(), 0); + QVERIFY(m_window->isOnAllDesktops()); QCOMPARE(m_window->plasmaVirtualDesktops().length(), 0); QVERIFY(m_window->isOnAllDesktops()); //return to the active desktop (0-1) m_windowInterface->setOnAllDesktops(false); virtualDesktopEnteredSpy.wait(); QCOMPARE(m_window->plasmaVirtualDesktops().length(), 1); QCOMPARE(m_windowInterface->plasmaVirtualDesktops().first(), QStringLiteral("0-1")); QVERIFY(!m_window->isOnAllDesktops()); - - //try setting on virtual desktops again but by setting every desktop by hand - m_windowInterface->addPlasmaVirtualDesktop(QStringLiteral("0-3")); - virtualDesktopEnteredSpy.wait(); - - virtualDesktopEnteredSpy.clear(); - virtualDesktopLeftSpy.clear(); - - m_windowInterface->addPlasmaVirtualDesktop(QStringLiteral("0-2")); - virtualDesktopLeftSpy.wait(); - QCOMPARE(virtualDesktopLeftSpy.count(), 2); - //note that virtualDesktopEntered should *not* have been emitted for 0-2 - QCOMPARE(virtualDesktopEnteredSpy.count(), 0); - - QCOMPARE(m_window->plasmaVirtualDesktops().length(), 0); - QVERIFY(m_window->isOnAllDesktops()); } void TestVirtualDesktop::testCreateRequested() { //rebuild some desktops testCreate(); QSignalSpy desktopCreateRequestedSpy(m_plasmaVirtualDesktopManagementInterface, &KWayland::Server::PlasmaVirtualDesktopManagementInterface::desktopCreateRequested); QSignalSpy desktopCreatedSpy(m_plasmaVirtualDesktopManagement, &PlasmaVirtualDesktopManagement::desktopCreated); //listen for createdRequested m_plasmaVirtualDesktopManagement->requestCreateVirtualDesktop(QStringLiteral("Desktop"), 1); desktopCreateRequestedSpy.wait(); QCOMPARE(desktopCreateRequestedSpy.first().first().toString(), QStringLiteral("Desktop")); QCOMPARE(desktopCreateRequestedSpy.first().at(1).toUInt(), (quint32)1); //actually create m_plasmaVirtualDesktopManagementInterface->createDesktop(QStringLiteral("0-4"), 1); KWayland::Server::PlasmaVirtualDesktopInterface *desktopInt = m_plasmaVirtualDesktopManagementInterface->desktops().at(1); QCOMPARE(desktopInt->id(), QStringLiteral("0-4")); desktopInt->setName(QStringLiteral("Desktop")); desktopCreatedSpy.wait(); QCOMPARE(desktopCreatedSpy.first().first().toString(), QStringLiteral("0-4")); QCOMPARE(m_plasmaVirtualDesktopManagement->desktops().count(), 4); PlasmaVirtualDesktop *desktop = m_plasmaVirtualDesktopManagement->desktops().at(1); QSignalSpy desktopDoneSpy(desktop, &PlasmaVirtualDesktop::done); desktopInt->sendDone(); // desktopDoneSpy.wait(); //check the order is correct QCOMPARE(m_plasmaVirtualDesktopManagement->desktops().at(0)->id(), QStringLiteral("0-1")); QCOMPARE(desktop->id(), QStringLiteral("0-4")); QCOMPARE(m_plasmaVirtualDesktopManagement->desktops().at(2)->id(), QStringLiteral("0-2")); QCOMPARE(m_plasmaVirtualDesktopManagement->desktops().at(3)->id(), QStringLiteral("0-3")); } void TestVirtualDesktop::testRemoveRequested() { //rebuild some desktops testCreate(); QSignalSpy desktopRemoveRequestedSpy(m_plasmaVirtualDesktopManagementInterface, &KWayland::Server::PlasmaVirtualDesktopManagementInterface::desktopRemoveRequested); //request a remove, just check the request arrived, ignore the request. m_plasmaVirtualDesktopManagement->requestRemoveVirtualDesktop(QStringLiteral("0-1")); desktopRemoveRequestedSpy.wait(); QCOMPARE(desktopRemoveRequestedSpy.first().first().toString(), QStringLiteral("0-1")); } QTEST_GUILESS_MAIN(TestVirtualDesktop) #include "test_plasma_virtual_desktop.moc" diff --git a/autotests/client/test_plasma_window_model.cpp b/autotests/client/test_plasma_window_model.cpp index cafaa17..1167d46 100644 --- a/autotests/client/test_plasma_window_model.cpp +++ b/autotests/client/test_plasma_window_model.cpp @@ -1,913 +1,935 @@ /******************************************************************** Copyright 2016 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ // Qt #include // client #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/registry.h" #include "../../src/client/plasmawindowmanagement.h" #include "../../src/client/plasmawindowmodel.h" // server #include "../../src/server/display.h" #include "../../src/server/plasmawindowmanagement_interface.h" +#include "../../src/server/plasmavirtualdesktop_interface.h" #include using namespace KWayland::Client; using namespace KWayland::Server; Q_DECLARE_METATYPE(Qt::MouseButton) typedef void (KWayland::Client::PlasmaWindow::*ClientWindowSignal)(); Q_DECLARE_METATYPE(ClientWindowSignal) typedef void (KWayland::Server::PlasmaWindowInterface::*ServerWindowBoolSetter)(bool); Q_DECLARE_METATYPE(ServerWindowBoolSetter) typedef void (KWayland::Server::PlasmaWindowInterface::*ServerWindowStringSetter)(const QString&); Q_DECLARE_METATYPE(ServerWindowStringSetter) typedef void (KWayland::Server::PlasmaWindowInterface::*ServerWindowQuint32Setter)(quint32); Q_DECLARE_METATYPE(ServerWindowQuint32Setter) typedef void (KWayland::Server::PlasmaWindowInterface::*ServerWindowVoidSetter)(); Q_DECLARE_METATYPE(ServerWindowVoidSetter) class PlasmaWindowModelTest : public QObject { Q_OBJECT private Q_SLOTS: void init(); void cleanup(); void testRoleNames_data(); void testRoleNames(); void testAddRemoveRows(); void testDefaultData_data(); void testDefaultData(); void testIsActive(); void testIsFullscreenable(); void testIsFullscreen(); void testIsMaximizable(); void testIsMaximized(); void testIsMinimizable(); void testIsMinimized(); void testIsKeepAbove(); void testIsKeepBelow(); - void testIsOnAllDesktops(); void testIsDemandingAttention(); void testSkipTaskbar(); void testSkipSwitcher(); void testIsShadeable(); void testIsShaded(); void testIsMovable(); void testIsResizable(); void testIsVirtualDesktopChangeable(); void testIsCloseable(); void testGeometry(); void testTitle(); void testAppId(); void testPid(); - void testVirtualDesktop(); + void testVirtualDesktops(); // TODO icon: can we ensure a theme is installed on CI? void testRequests(); // TODO: minimized geometry // TODO: model reset void testCreateWithUnmappedWindow(); void testChangeWindowAfterModelDestroy_data(); void testChangeWindowAfterModelDestroy(); void testCreateWindowAfterModelDestroy(); private: bool testBooleanData(PlasmaWindowModel::AdditionalRoles role, void (PlasmaWindowInterface::*function)(bool)); Display *m_display = nullptr; PlasmaWindowManagementInterface *m_pwInterface = nullptr; PlasmaWindowManagement *m_pw = nullptr; + KWayland::Server::PlasmaVirtualDesktopManagementInterface *m_plasmaVirtualDesktopManagementInterface = nullptr; ConnectionThread *m_connection = nullptr; QThread *m_thread = nullptr; EventQueue *m_queue = nullptr; }; static const QString s_socketName = QStringLiteral("kwayland-test-fake-input-0"); void PlasmaWindowModelTest::init() { delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); m_display->createShm(); m_pwInterface = m_display->createPlasmaWindowManagement(); m_pwInterface->create(); + m_plasmaVirtualDesktopManagementInterface = m_display->createPlasmaVirtualDesktopManagement(m_display); + m_plasmaVirtualDesktopManagementInterface->create(); + QVERIFY(m_plasmaVirtualDesktopManagementInterface->isValid()); + m_plasmaVirtualDesktopManagementInterface->createDesktop("desktop1"); + m_plasmaVirtualDesktopManagementInterface->createDesktop("desktop2"); + m_pwInterface->setPlasmaVirtualDesktopManagementInterface(m_plasmaVirtualDesktopManagementInterface); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new EventQueue(this); m_queue->setup(m_connection); Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); registry.setEventQueue(m_queue); registry.create(m_connection); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(interfacesAnnouncedSpy.wait()); m_pw = registry.createPlasmaWindowManagement(registry.interface(Registry::Interface::PlasmaWindowManagement).name, registry.interface(Registry::Interface::PlasmaWindowManagement).version, this); QVERIFY(m_pw->isValid()); } void PlasmaWindowModelTest::cleanup() { #define CLEANUP(variable) \ if (variable) { \ delete variable; \ variable = nullptr; \ } CLEANUP(m_pw) + CLEANUP(m_plasmaVirtualDesktopManagementInterface) CLEANUP(m_queue) if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } CLEANUP(m_pwInterface) CLEANUP(m_display) #undef CLEANUP } bool PlasmaWindowModelTest::testBooleanData(PlasmaWindowModel::AdditionalRoles role, void (PlasmaWindowInterface::*function)(bool)) { #define VERIFY(statement) \ if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__))\ return false; #define COMPARE(actual, expected) \ if (!QTest::qCompare(actual, expected, #actual, #expected, __FILE__, __LINE__))\ return false; auto model = m_pw->createWindowModel(); VERIFY(model); QSignalSpy rowInsertedSpy(model, &PlasmaWindowModel::rowsInserted); VERIFY(rowInsertedSpy.isValid()); auto w = m_pwInterface->createWindow(m_pwInterface); VERIFY(w); VERIFY(rowInsertedSpy.wait()); m_connection->flush(); m_display->dispatchEvents(); QSignalSpy dataChangedSpy(model, &PlasmaWindowModel::dataChanged); VERIFY(dataChangedSpy.isValid()); const QModelIndex index = model->index(0); COMPARE(model->data(index, role).toBool(), false); (w->*(function))(true); VERIFY(dataChangedSpy.wait()); COMPARE(dataChangedSpy.count(), 1); COMPARE(dataChangedSpy.last().first().toModelIndex(), index); COMPARE(dataChangedSpy.last().last().value>(), QVector{int(role)}); COMPARE(model->data(index, role).toBool(), true); (w->*(function))(false); VERIFY(dataChangedSpy.wait()); COMPARE(dataChangedSpy.count(), 2); COMPARE(dataChangedSpy.last().first().toModelIndex(), index); COMPARE(dataChangedSpy.last().last().value>(), QVector{int(role)}); COMPARE(model->data(index, role).toBool(), false); #undef COMPARE #undef VERIFY return true; } void PlasmaWindowModelTest::testRoleNames_data() { QTest::addColumn("role"); QTest::addColumn("name"); QTest::newRow("display") << int(Qt::DisplayRole) << QByteArrayLiteral("DisplayRole"); QTest::newRow("decoration") << int(Qt::DecorationRole) << QByteArrayLiteral("DecorationRole"); QTest::newRow("AppId") << int(PlasmaWindowModel::AppId) << QByteArrayLiteral("AppId"); QTest::newRow("Pid") << int(PlasmaWindowModel::Pid) << QByteArrayLiteral("Pid"); QTest::newRow("IsActive") << int(PlasmaWindowModel::IsActive) << QByteArrayLiteral("IsActive"); QTest::newRow("IsFullscreenable") << int(PlasmaWindowModel::IsFullscreenable) << QByteArrayLiteral("IsFullscreenable"); QTest::newRow("IsFullscreen") << int(PlasmaWindowModel::IsFullscreen) << QByteArrayLiteral("IsFullscreen"); QTest::newRow("IsMaximizable") << int(PlasmaWindowModel::IsMaximizable) << QByteArrayLiteral("IsMaximizable"); QTest::newRow("IsMaximized") << int(PlasmaWindowModel::IsMaximized) << QByteArrayLiteral("IsMaximized"); QTest::newRow("IsMinimizable") << int(PlasmaWindowModel::IsMinimizable) << QByteArrayLiteral("IsMinimizable"); QTest::newRow("IsMinimized") << int(PlasmaWindowModel::IsMinimized) << QByteArrayLiteral("IsMinimized"); QTest::newRow("IsKeepAbove") << int(PlasmaWindowModel::IsKeepAbove) << QByteArrayLiteral("IsKeepAbove"); QTest::newRow("IsKeepBelow") << int(PlasmaWindowModel::IsKeepBelow) << QByteArrayLiteral("IsKeepBelow"); QTest::newRow("VirtualDesktop") << int(PlasmaWindowModel::VirtualDesktop) << QByteArrayLiteral("VirtualDesktop"); QTest::newRow("IsOnAllDesktops") << int(PlasmaWindowModel::IsOnAllDesktops) << QByteArrayLiteral("IsOnAllDesktops"); QTest::newRow("IsDemandingAttention") << int(PlasmaWindowModel::IsDemandingAttention) << QByteArrayLiteral("IsDemandingAttention"); QTest::newRow("SkipTaskbar") << int(PlasmaWindowModel::SkipTaskbar) << QByteArrayLiteral("SkipTaskbar"); QTest::newRow("SkipSwitcher") << int(PlasmaWindowModel::SkipSwitcher) << QByteArrayLiteral("SkipSwitcher"); QTest::newRow("IsShadeable") << int(PlasmaWindowModel::IsShadeable) << QByteArrayLiteral("IsShadeable"); QTest::newRow("IsShaded") << int(PlasmaWindowModel::IsShaded) << QByteArrayLiteral("IsShaded"); QTest::newRow("IsMovable") << int(PlasmaWindowModel::IsMovable) << QByteArrayLiteral("IsMovable"); QTest::newRow("IsResizable") << int(PlasmaWindowModel::IsResizable) << QByteArrayLiteral("IsResizable"); QTest::newRow("IsVirtualDesktopChangeable") << int(PlasmaWindowModel::IsVirtualDesktopChangeable) << QByteArrayLiteral("IsVirtualDesktopChangeable"); QTest::newRow("IsCloseable") << int(PlasmaWindowModel::IsCloseable) << QByteArrayLiteral("IsCloseable"); QTest::newRow("Geometry") << int(PlasmaWindowModel::Geometry) << QByteArrayLiteral("Geometry"); } void PlasmaWindowModelTest::testRoleNames() { // just verifies that all role names are available auto model = m_pw->createWindowModel(); QVERIFY(model); const auto roles = model->roleNames(); QFETCH(int, role); auto it = roles.find(role); QVERIFY(it != roles.end()); QTEST(it.value(), "name"); } void PlasmaWindowModelTest::testAddRemoveRows() { // this test verifies that adding/removing rows to the Model works auto model = m_pw->createWindowModel(); QVERIFY(model); QCOMPARE(model->rowCount(), 0); QVERIFY(!model->index(0).isValid()); // now let's add a row QSignalSpy rowInsertedSpy(model, &PlasmaWindowModel::rowsInserted); QVERIFY(rowInsertedSpy.isValid()); // this happens by creating a PlasmaWindow on server side auto w = m_pwInterface->createWindow(m_pwInterface); QVERIFY(w); QVERIFY(rowInsertedSpy.wait()); QCOMPARE(rowInsertedSpy.count(), 1); QVERIFY(!rowInsertedSpy.first().at(0).toModelIndex().isValid()); QCOMPARE(rowInsertedSpy.first().at(1).toInt(), 0); QCOMPARE(rowInsertedSpy.first().at(2).toInt(), 0); // the model should have a row now QCOMPARE(model->rowCount(), 1); QVERIFY(model->index(0).isValid()); // that index doesn't have children QCOMPARE(model->rowCount(model->index(0)), 0); // process events in order to ensure that the resource is created on server side before we unmap the window QCoreApplication::instance()->processEvents(QEventLoop::WaitForMoreEvents); // now let's remove that again QSignalSpy rowRemovedSpy(model, &PlasmaWindowModel::rowsRemoved); QVERIFY(rowRemovedSpy.isValid()); w->unmap(); QVERIFY(rowRemovedSpy.wait()); QCOMPARE(rowRemovedSpy.count(), 1); QVERIFY(!rowRemovedSpy.first().at(0).toModelIndex().isValid()); QCOMPARE(rowRemovedSpy.first().at(1).toInt(), 0); QCOMPARE(rowRemovedSpy.first().at(2).toInt(), 0); // now the model is empty again QCOMPARE(model->rowCount(), 0); QVERIFY(!model->index(0).isValid()); QSignalSpy wDestroyedSpy(w, &QObject::destroyed); QVERIFY(wDestroyedSpy.isValid()); QVERIFY(wDestroyedSpy.wait()); } void PlasmaWindowModelTest::testDefaultData_data() { QTest::addColumn("role"); QTest::addColumn("value"); QTest::newRow("display") << int(Qt::DisplayRole) << QVariant(QString()); QTest::newRow("decoration") << int(Qt::DecorationRole) << QVariant(QIcon()); QTest::newRow("AppId") << int(PlasmaWindowModel::AppId) << QVariant(QString()); QTest::newRow("IsActive") << int(PlasmaWindowModel::IsActive) << QVariant(false); QTest::newRow("IsFullscreenable") << int(PlasmaWindowModel::IsFullscreenable) << QVariant(false); QTest::newRow("IsFullscreen") << int(PlasmaWindowModel::IsFullscreen) << QVariant(false); QTest::newRow("IsMaximizable") << int(PlasmaWindowModel::IsMaximizable) << QVariant(false); QTest::newRow("IsMaximized") << int(PlasmaWindowModel::IsMaximized) << QVariant(false); QTest::newRow("IsMinimizable") << int(PlasmaWindowModel::IsMinimizable) << QVariant(false); QTest::newRow("IsMinimized") << int(PlasmaWindowModel::IsMinimized) << QVariant(false); QTest::newRow("IsKeepAbove") << int(PlasmaWindowModel::IsKeepAbove) << QVariant(false); QTest::newRow("IsKeepBelow") << int(PlasmaWindowModel::IsKeepBelow) << QVariant(false); QTest::newRow("VirtualDesktop") << int(PlasmaWindowModel::VirtualDesktop) << QVariant(0); QTest::newRow("IsOnAllDesktops") << int(PlasmaWindowModel::IsOnAllDesktops) << QVariant(true); QTest::newRow("IsDemandingAttention") << int(PlasmaWindowModel::IsDemandingAttention) << QVariant(false); QTest::newRow("IsShadeable") << int(PlasmaWindowModel::IsShadeable) << QVariant(false); QTest::newRow("IsShaded") << int(PlasmaWindowModel::IsShaded) << QVariant(false); QTest::newRow("SkipTaskbar") << int(PlasmaWindowModel::SkipTaskbar) << QVariant(false); QTest::newRow("IsMovable") << int(PlasmaWindowModel::IsMovable) << QVariant(false); QTest::newRow("IsResizable") << int(PlasmaWindowModel::IsResizable) << QVariant(false); QTest::newRow("IsVirtualDesktopChangeable") << int(PlasmaWindowModel::IsVirtualDesktopChangeable) << QVariant(false); QTest::newRow("IsCloseable") << int(PlasmaWindowModel::IsCloseable) << QVariant(false); QTest::newRow("Geometry") << int(PlasmaWindowModel::Geometry) << QVariant(QRect()); QTest::newRow("Pid") << int(PlasmaWindowModel::Pid) << QVariant(0); } void PlasmaWindowModelTest::testDefaultData() { // this test validates the default data of a PlasmaWindow without having set any values // first create a model with a window auto model = m_pw->createWindowModel(); QVERIFY(model); QSignalSpy rowInsertedSpy(model, &PlasmaWindowModel::rowsInserted); QVERIFY(rowInsertedSpy.isValid()); auto w = m_pwInterface->createWindow(m_pwInterface); QVERIFY(w); QVERIFY(rowInsertedSpy.wait()); QModelIndex index = model->index(0); QFETCH(int, role); QTEST(model->data(index, role), "value"); } void PlasmaWindowModelTest::testIsActive() { QVERIFY(testBooleanData(PlasmaWindowModel::IsActive, &PlasmaWindowInterface::setActive)); } void PlasmaWindowModelTest::testIsFullscreenable() { QVERIFY(testBooleanData(PlasmaWindowModel::IsFullscreenable, &PlasmaWindowInterface::setFullscreenable)); } void PlasmaWindowModelTest::testIsFullscreen() { QVERIFY(testBooleanData(PlasmaWindowModel::IsFullscreen, &PlasmaWindowInterface::setFullscreen)); } void PlasmaWindowModelTest::testIsMaximizable() { QVERIFY(testBooleanData(PlasmaWindowModel::IsMaximizable, &PlasmaWindowInterface::setMaximizeable)); } void PlasmaWindowModelTest::testIsMaximized() { QVERIFY(testBooleanData(PlasmaWindowModel::IsMaximized, &PlasmaWindowInterface::setMaximized)); } void PlasmaWindowModelTest::testIsMinimizable() { QVERIFY(testBooleanData(PlasmaWindowModel::IsMinimizable, &PlasmaWindowInterface::setMinimizeable)); } void PlasmaWindowModelTest::testIsMinimized() { QVERIFY(testBooleanData(PlasmaWindowModel::IsMinimized, &PlasmaWindowInterface::setMinimized)); } void PlasmaWindowModelTest::testIsKeepAbove() { QVERIFY(testBooleanData(PlasmaWindowModel::IsKeepAbove, &PlasmaWindowInterface::setKeepAbove)); } void PlasmaWindowModelTest::testIsKeepBelow() { QVERIFY(testBooleanData(PlasmaWindowModel::IsKeepBelow, &PlasmaWindowInterface::setKeepBelow)); } -void PlasmaWindowModelTest::testIsOnAllDesktops() -{ - QVERIFY(testBooleanData(PlasmaWindowModel::IsOnAllDesktops, &PlasmaWindowInterface::setOnAllDesktops)); -} - void PlasmaWindowModelTest::testIsDemandingAttention() { QVERIFY(testBooleanData(PlasmaWindowModel::IsDemandingAttention, &PlasmaWindowInterface::setDemandsAttention)); } void PlasmaWindowModelTest::testSkipTaskbar() { QVERIFY(testBooleanData(PlasmaWindowModel::SkipTaskbar, &PlasmaWindowInterface::setSkipTaskbar)); } void PlasmaWindowModelTest::testSkipSwitcher() { QVERIFY(testBooleanData(PlasmaWindowModel::SkipSwitcher, &PlasmaWindowInterface::setSkipSwitcher)); } void PlasmaWindowModelTest::testIsShadeable() { QVERIFY(testBooleanData(PlasmaWindowModel::IsShadeable, &PlasmaWindowInterface::setShadeable)); } void PlasmaWindowModelTest::testIsShaded() { QVERIFY(testBooleanData(PlasmaWindowModel::IsShaded, &PlasmaWindowInterface::setShaded)); } void PlasmaWindowModelTest::testIsMovable() { QVERIFY(testBooleanData(PlasmaWindowModel::IsMovable, &PlasmaWindowInterface::setMovable)); } void PlasmaWindowModelTest::testIsResizable() { QVERIFY(testBooleanData(PlasmaWindowModel::IsResizable, &PlasmaWindowInterface::setResizable)); } void PlasmaWindowModelTest::testIsVirtualDesktopChangeable() { QVERIFY(testBooleanData(PlasmaWindowModel::IsVirtualDesktopChangeable, &PlasmaWindowInterface::setVirtualDesktopChangeable)); } void PlasmaWindowModelTest::testIsCloseable() { QVERIFY(testBooleanData(PlasmaWindowModel::IsCloseable, &PlasmaWindowInterface::setCloseable)); } void PlasmaWindowModelTest::testGeometry() { auto model = m_pw->createWindowModel(); QVERIFY(model); QSignalSpy rowInsertedSpy(model, &PlasmaWindowModel::rowsInserted); QVERIFY(rowInsertedSpy.isValid()); auto w = m_pwInterface->createWindow(m_pwInterface); QVERIFY(w); QVERIFY(rowInsertedSpy.wait()); const QModelIndex index = model->index(0); QCOMPARE(model->data(index, PlasmaWindowModel::Geometry).toRect(), QRect()); QSignalSpy dataChangedSpy(model, &PlasmaWindowModel::dataChanged); QVERIFY(dataChangedSpy.isValid()); const QRect geom(0, 15, 50, 75); w->setGeometry(geom); QVERIFY(dataChangedSpy.wait()); QCOMPARE(dataChangedSpy.count(), 1); QCOMPARE(dataChangedSpy.last().first().toModelIndex(), index); QCOMPARE(dataChangedSpy.last().last().value>(), QVector{int(PlasmaWindowModel::Geometry)}); QCOMPARE(model->data(index, PlasmaWindowModel::Geometry).toRect(), geom); } void PlasmaWindowModelTest::testTitle() { auto model = m_pw->createWindowModel(); QVERIFY(model); QSignalSpy rowInsertedSpy(model, &PlasmaWindowModel::rowsInserted); QVERIFY(rowInsertedSpy.isValid()); auto w = m_pwInterface->createWindow(m_pwInterface); QVERIFY(w); QVERIFY(rowInsertedSpy.wait()); m_connection->flush(); m_display->dispatchEvents(); QSignalSpy dataChangedSpy(model, &PlasmaWindowModel::dataChanged); QVERIFY(dataChangedSpy.isValid()); const QModelIndex index = model->index(0); QCOMPARE(model->data(index, Qt::DisplayRole).toString(), QString()); w->setTitle(QStringLiteral("foo")); QVERIFY(dataChangedSpy.wait()); QCOMPARE(dataChangedSpy.count(), 1); QCOMPARE(dataChangedSpy.last().first().toModelIndex(), index); QCOMPARE(dataChangedSpy.last().last().value>(), QVector{int(Qt::DisplayRole)}); QCOMPARE(model->data(index, Qt::DisplayRole).toString(), QStringLiteral("foo")); } void PlasmaWindowModelTest::testAppId() { auto model = m_pw->createWindowModel(); QVERIFY(model); QSignalSpy rowInsertedSpy(model, &PlasmaWindowModel::rowsInserted); QVERIFY(rowInsertedSpy.isValid()); auto w = m_pwInterface->createWindow(m_pwInterface); QVERIFY(w); QVERIFY(rowInsertedSpy.wait()); m_connection->flush(); m_display->dispatchEvents(); QSignalSpy dataChangedSpy(model, &PlasmaWindowModel::dataChanged); QVERIFY(dataChangedSpy.isValid()); const QModelIndex index = model->index(0); QCOMPARE(model->data(index, PlasmaWindowModel::AppId).toString(), QString()); w->setAppId(QStringLiteral("org.kde.testapp")); QVERIFY(dataChangedSpy.wait()); QCOMPARE(dataChangedSpy.count(), 1); QCOMPARE(dataChangedSpy.last().first().toModelIndex(), index); QCOMPARE(dataChangedSpy.last().last().value>(), QVector{int(PlasmaWindowModel::AppId)}); QCOMPARE(model->data(index, PlasmaWindowModel::AppId).toString(), QStringLiteral("org.kde.testapp")); } void PlasmaWindowModelTest::testPid() { auto model = m_pw->createWindowModel(); QVERIFY(model); QSignalSpy rowInsertedSpy(model, &PlasmaWindowModel::rowsInserted); QVERIFY(rowInsertedSpy.isValid()); auto w = m_pwInterface->createWindow(m_pwInterface); w->setPid(1337); QVERIFY(w); m_connection->flush(); m_display->dispatchEvents(); QVERIFY(rowInsertedSpy.wait()); //pid should be set as soon as the new row appears const QModelIndex index = model->index(0); QCOMPARE(model->data(index, PlasmaWindowModel::Pid).toInt(), 1337); } -void PlasmaWindowModelTest::testVirtualDesktop() +void PlasmaWindowModelTest::testVirtualDesktops() { auto model = m_pw->createWindowModel(); QVERIFY(model); QSignalSpy rowInsertedSpy(model, &PlasmaWindowModel::rowsInserted); QVERIFY(rowInsertedSpy.isValid()); auto w = m_pwInterface->createWindow(m_pwInterface); QVERIFY(w); QVERIFY(rowInsertedSpy.wait()); m_connection->flush(); m_display->dispatchEvents(); QSignalSpy dataChangedSpy(model, &PlasmaWindowModel::dataChanged); QVERIFY(dataChangedSpy.isValid()); const QModelIndex index = model->index(0); - QCOMPARE(model->data(index, PlasmaWindowModel::VirtualDesktop).toInt(), 0); + QCOMPARE(model->data(index, PlasmaWindowModel::VirtualDesktops).toStringList(), QStringList()); + + w->addPlasmaVirtualDesktop("desktop1"); + QVERIFY(dataChangedSpy.wait()); + QCOMPARE(dataChangedSpy.count(), 2); + QCOMPARE(dataChangedSpy.first().first().toModelIndex(), index); + QCOMPARE(dataChangedSpy.last().first().toModelIndex(), index); + + QCOMPARE(dataChangedSpy.first().last().value>(), QVector{int(PlasmaWindowModel::VirtualDesktops)}); + QCOMPARE(dataChangedSpy.last().last().value>(), QVector{int(PlasmaWindowModel::IsOnAllDesktops)}); + + QCOMPARE(model->data(index, PlasmaWindowModel::VirtualDesktops).toStringList(), QStringList({"desktop1"})); + QCOMPARE(model->data(index, PlasmaWindowModel::IsOnAllDesktops).toBool(), false); - w->setVirtualDesktop(1); + dataChangedSpy.clear(); + w->addPlasmaVirtualDesktop("desktop2"); QVERIFY(dataChangedSpy.wait()); QCOMPARE(dataChangedSpy.count(), 1); QCOMPARE(dataChangedSpy.last().first().toModelIndex(), index); - QCOMPARE(dataChangedSpy.last().last().value>(), QVector{int(PlasmaWindowModel::VirtualDesktop)}); - QCOMPARE(model->data(index, PlasmaWindowModel::VirtualDesktop).toInt(), 1); + QCOMPARE(dataChangedSpy.last().last().value>(), QVector{int(PlasmaWindowModel::VirtualDesktops)}); + QCOMPARE(model->data(index, PlasmaWindowModel::VirtualDesktops).toStringList(), QStringList({"desktop1", "desktop2"})); + QCOMPARE(model->data(index, PlasmaWindowModel::IsOnAllDesktops).toBool(), false); + + w->removePlasmaVirtualDesktop("desktop2"); + w->removePlasmaVirtualDesktop("desktop1"); + QVERIFY(dataChangedSpy.wait()); + QCOMPARE(dataChangedSpy.last().last().value>(), QVector{int(PlasmaWindowModel::IsOnAllDesktops)}); + QCOMPARE(model->data(index, PlasmaWindowModel::VirtualDesktops).toStringList(), QStringList({})); + QCOMPARE(model->data(index, PlasmaWindowModel::IsOnAllDesktops).toBool(), true); - // setting to same should not trigger - w->setVirtualDesktop(1); QVERIFY(!dataChangedSpy.wait(100)); } void PlasmaWindowModelTest::testRequests() { // this test verifies that the various requests are properly passed to the server auto model = m_pw->createWindowModel(); QVERIFY(model); QSignalSpy rowInsertedSpy(model, &PlasmaWindowModel::rowsInserted); QVERIFY(rowInsertedSpy.isValid()); auto w = m_pwInterface->createWindow(m_pwInterface); QVERIFY(w); QVERIFY(rowInsertedSpy.wait()); QSignalSpy activateRequestedSpy(w, &PlasmaWindowInterface::activeRequested); QVERIFY(activateRequestedSpy.isValid()); QSignalSpy closeRequestedSpy(w, &PlasmaWindowInterface::closeRequested); QVERIFY(closeRequestedSpy.isValid()); QSignalSpy moveRequestedSpy(w, &PlasmaWindowInterface::moveRequested); QVERIFY(moveRequestedSpy.isValid()); QSignalSpy resizeRequestedSpy(w, &PlasmaWindowInterface::resizeRequested); QVERIFY(resizeRequestedSpy.isValid()); QSignalSpy virtualDesktopRequestedSpy(w, &PlasmaWindowInterface::virtualDesktopRequested); QVERIFY(virtualDesktopRequestedSpy.isValid()); QSignalSpy keepAboveRequestedSpy(w, &PlasmaWindowInterface::keepAboveRequested); QVERIFY(keepAboveRequestedSpy.isValid()); QSignalSpy keepBelowRequestedSpy(w, &PlasmaWindowInterface::keepBelowRequested); QVERIFY(keepBelowRequestedSpy.isValid()); QSignalSpy minimizedRequestedSpy(w, &PlasmaWindowInterface::minimizedRequested); QVERIFY(minimizedRequestedSpy.isValid()); QSignalSpy maximizeRequestedSpy(w, &PlasmaWindowInterface::maximizedRequested); QVERIFY(maximizeRequestedSpy.isValid()); QSignalSpy shadeRequestedSpy(w, &PlasmaWindowInterface::shadedRequested); QVERIFY(shadeRequestedSpy.isValid()); // first let's use some invalid row numbers model->requestActivate(-1); model->requestClose(-1); model->requestVirtualDesktop(-1, 1); model->requestToggleKeepAbove(-1); model->requestToggleKeepBelow(-1); model->requestToggleMinimized(-1); model->requestToggleMaximized(-1); model->requestActivate(1); model->requestClose(1); model->requestMove(1); model->requestResize(1); model->requestVirtualDesktop(1, 1); model->requestToggleKeepAbove(1); model->requestToggleKeepBelow(1); model->requestToggleMinimized(1); model->requestToggleMaximized(1); model->requestToggleShaded(1); // that should not have triggered any signals QVERIFY(!activateRequestedSpy.wait(100)); QVERIFY(activateRequestedSpy.isEmpty()); QVERIFY(closeRequestedSpy.isEmpty()); QVERIFY(moveRequestedSpy.isEmpty()); QVERIFY(resizeRequestedSpy.isEmpty()); QVERIFY(virtualDesktopRequestedSpy.isEmpty()); QVERIFY(minimizedRequestedSpy.isEmpty()); QVERIFY(maximizeRequestedSpy.isEmpty()); QVERIFY(shadeRequestedSpy.isEmpty()); // now with the proper row // activate model->requestActivate(0); QVERIFY(activateRequestedSpy.wait()); QCOMPARE(activateRequestedSpy.count(), 1); QCOMPARE(activateRequestedSpy.first().first().toBool(), true); QCOMPARE(closeRequestedSpy.count(), 0); QCOMPARE(moveRequestedSpy.count(), 0); QCOMPARE(resizeRequestedSpy.count(), 0); QCOMPARE(virtualDesktopRequestedSpy.count(), 0); QCOMPARE(minimizedRequestedSpy.count(), 0); QCOMPARE(maximizeRequestedSpy.count(), 0); QCOMPARE(shadeRequestedSpy.count(), 0); // close model->requestClose(0); QVERIFY(closeRequestedSpy.wait()); QCOMPARE(activateRequestedSpy.count(), 1); QCOMPARE(closeRequestedSpy.count(), 1); QCOMPARE(moveRequestedSpy.count(), 0); QCOMPARE(resizeRequestedSpy.count(), 0); QCOMPARE(virtualDesktopRequestedSpy.count(), 0); QCOMPARE(minimizedRequestedSpy.count(), 0); QCOMPARE(maximizeRequestedSpy.count(), 0); QCOMPARE(shadeRequestedSpy.count(), 0); // move model->requestMove(0); QVERIFY(moveRequestedSpy.wait()); QCOMPARE(activateRequestedSpy.count(), 1); QCOMPARE(closeRequestedSpy.count(), 1); QCOMPARE(moveRequestedSpy.count(), 1); QCOMPARE(resizeRequestedSpy.count(), 0); QCOMPARE(virtualDesktopRequestedSpy.count(), 0); QCOMPARE(minimizedRequestedSpy.count(), 0); QCOMPARE(maximizeRequestedSpy.count(), 0); QCOMPARE(shadeRequestedSpy.count(), 0); // resize model->requestResize(0); QVERIFY(resizeRequestedSpy.wait()); QCOMPARE(activateRequestedSpy.count(), 1); QCOMPARE(closeRequestedSpy.count(), 1); QCOMPARE(moveRequestedSpy.count(), 1); QCOMPARE(resizeRequestedSpy.count(), 1); QCOMPARE(virtualDesktopRequestedSpy.count(), 0); QCOMPARE(minimizedRequestedSpy.count(), 0); QCOMPARE(maximizeRequestedSpy.count(), 0); QCOMPARE(shadeRequestedSpy.count(), 0); // virtual desktop model->requestVirtualDesktop(0, 1); QVERIFY(virtualDesktopRequestedSpy.wait()); QCOMPARE(virtualDesktopRequestedSpy.count(), 1); QCOMPARE(virtualDesktopRequestedSpy.first().first().toUInt(), 1u); QCOMPARE(activateRequestedSpy.count(), 1); QCOMPARE(closeRequestedSpy.count(), 1); QCOMPARE(moveRequestedSpy.count(), 1); QCOMPARE(resizeRequestedSpy.count(), 1); QCOMPARE(minimizedRequestedSpy.count(), 0); QCOMPARE(maximizeRequestedSpy.count(), 0); QCOMPARE(shadeRequestedSpy.count(), 0); // keep above model->requestToggleKeepAbove(0); QVERIFY(keepAboveRequestedSpy.wait()); QCOMPARE(keepAboveRequestedSpy.count(), 1); QCOMPARE(keepAboveRequestedSpy.first().first().toBool(), true); QCOMPARE(activateRequestedSpy.count(), 1); QCOMPARE(closeRequestedSpy.count(), 1); QCOMPARE(moveRequestedSpy.count(), 1); QCOMPARE(resizeRequestedSpy.count(), 1); QCOMPARE(virtualDesktopRequestedSpy.count(), 1); QCOMPARE(maximizeRequestedSpy.count(), 0); QCOMPARE(shadeRequestedSpy.count(), 0); // keep Below model->requestToggleKeepBelow(0); QVERIFY(keepBelowRequestedSpy.wait()); QCOMPARE(keepBelowRequestedSpy.count(), 1); QCOMPARE(keepBelowRequestedSpy.first().first().toBool(), true); QCOMPARE(activateRequestedSpy.count(), 1); QCOMPARE(closeRequestedSpy.count(), 1); QCOMPARE(moveRequestedSpy.count(), 1); QCOMPARE(resizeRequestedSpy.count(), 1); QCOMPARE(virtualDesktopRequestedSpy.count(), 1); QCOMPARE(maximizeRequestedSpy.count(), 0); QCOMPARE(shadeRequestedSpy.count(), 0); // minimize model->requestToggleMinimized(0); QVERIFY(minimizedRequestedSpy.wait()); QCOMPARE(minimizedRequestedSpy.count(), 1); QCOMPARE(minimizedRequestedSpy.first().first().toBool(), true); QCOMPARE(activateRequestedSpy.count(), 1); QCOMPARE(closeRequestedSpy.count(), 1); QCOMPARE(moveRequestedSpy.count(), 1); QCOMPARE(resizeRequestedSpy.count(), 1); QCOMPARE(virtualDesktopRequestedSpy.count(), 1); QCOMPARE(maximizeRequestedSpy.count(), 0); QCOMPARE(shadeRequestedSpy.count(), 0); // maximize model->requestToggleMaximized(0); QVERIFY(maximizeRequestedSpy.wait()); QCOMPARE(maximizeRequestedSpy.count(), 1); QCOMPARE(maximizeRequestedSpy.first().first().toBool(), true); QCOMPARE(activateRequestedSpy.count(), 1); QCOMPARE(closeRequestedSpy.count(), 1); QCOMPARE(moveRequestedSpy.count(), 1); QCOMPARE(virtualDesktopRequestedSpy.count(), 1); QCOMPARE(minimizedRequestedSpy.count(), 1); QCOMPARE(shadeRequestedSpy.count(), 0); // shade model->requestToggleShaded(0); QVERIFY(shadeRequestedSpy.wait()); QCOMPARE(shadeRequestedSpy.count(), 1); QCOMPARE(shadeRequestedSpy.first().first().toBool(), true); QCOMPARE(activateRequestedSpy.count(), 1); QCOMPARE(closeRequestedSpy.count(), 1); QCOMPARE(moveRequestedSpy.count(), 1); QCOMPARE(resizeRequestedSpy.count(), 1); QCOMPARE(virtualDesktopRequestedSpy.count(), 1); QCOMPARE(minimizedRequestedSpy.count(), 1); QCOMPARE(maximizeRequestedSpy.count(), 1); // the toggles can also support a different state QSignalSpy dataChangedSpy(model, &PlasmaWindowModel::dataChanged); QVERIFY(dataChangedSpy.isValid()); // keepAbove w->setKeepAbove(true); QVERIFY(dataChangedSpy.wait()); model->requestToggleKeepAbove(0); QVERIFY(keepAboveRequestedSpy.wait()); QCOMPARE(keepAboveRequestedSpy.count(), 2); QCOMPARE(keepAboveRequestedSpy.last().first().toBool(), false); // keepBelow w->setKeepBelow(true); QVERIFY(dataChangedSpy.wait()); model->requestToggleKeepBelow(0); QVERIFY(keepBelowRequestedSpy.wait()); QCOMPARE(keepBelowRequestedSpy.count(), 2); QCOMPARE(keepBelowRequestedSpy.last().first().toBool(), false); // minimize w->setMinimized(true); QVERIFY(dataChangedSpy.wait()); model->requestToggleMinimized(0); QVERIFY(minimizedRequestedSpy.wait()); QCOMPARE(minimizedRequestedSpy.count(), 2); QCOMPARE(minimizedRequestedSpy.last().first().toBool(), false); // maximized w->setMaximized(true); QVERIFY(dataChangedSpy.wait()); model->requestToggleMaximized(0); QVERIFY(maximizeRequestedSpy.wait()); QCOMPARE(maximizeRequestedSpy.count(), 2); QCOMPARE(maximizeRequestedSpy.last().first().toBool(), false); // shaded w->setShaded(true); QVERIFY(dataChangedSpy.wait()); model->requestToggleShaded(0); QVERIFY(shadeRequestedSpy.wait()); QCOMPARE(shadeRequestedSpy.count(), 2); QCOMPARE(shadeRequestedSpy.last().first().toBool(), false); } void PlasmaWindowModelTest::testCreateWithUnmappedWindow() { // this test verifies that creating the model just when an unmapped window exists doesn't cause problems // that is the unmapped window should be added (as expected), but also be removed again // create a window in "normal way" QSignalSpy windowCreatedSpy(m_pw, &PlasmaWindowManagement::windowCreated); QVERIFY(windowCreatedSpy.isValid()); auto w = m_pwInterface->createWindow(m_pwInterface); QVERIFY(w); QVERIFY(windowCreatedSpy.wait()); PlasmaWindow *window = windowCreatedSpy.first().first().value(); QVERIFY(window); // make sure the resource is properly created on server side QCoreApplication::instance()->processEvents(QEventLoop::WaitForMoreEvents); QSignalSpy unmappedSpy(window, &PlasmaWindow::unmapped); QVERIFY(unmappedSpy.isValid()); QSignalSpy destroyedSpy(window, &PlasmaWindow::destroyed); QVERIFY(destroyedSpy.isValid()); // unmap should be triggered, but not yet the destroyed w->unmap(); QVERIFY(unmappedSpy.wait()); QVERIFY(destroyedSpy.isEmpty()); auto model = m_pw->createWindowModel(); QVERIFY(model); QCOMPARE(model->rowCount(), 1); QSignalSpy rowRemovedSpy(model, &PlasmaWindowModel::rowsRemoved); QVERIFY(rowRemovedSpy.isValid()); QVERIFY(rowRemovedSpy.wait()); QCOMPARE(rowRemovedSpy.count(), 1); QCOMPARE(model->rowCount(), 0); QCOMPARE(destroyedSpy.count(), 1); } void PlasmaWindowModelTest::testChangeWindowAfterModelDestroy_data() { QTest::addColumn("changedSignal"); QTest::addColumn("setter"); QTest::addColumn("value"); QTest::newRow("active") << &PlasmaWindow::activeChanged << qVariantFromValue(&PlasmaWindowInterface::setActive) << QVariant(true); QTest::newRow("minimized") << &PlasmaWindow::minimizedChanged << qVariantFromValue(&PlasmaWindowInterface::setMinimized) << QVariant(true); QTest::newRow("fullscreen") << &PlasmaWindow::fullscreenChanged << qVariantFromValue(&PlasmaWindowInterface::setFullscreen) << QVariant(true); QTest::newRow("keepAbove") << &PlasmaWindow::keepAboveChanged << qVariantFromValue(&PlasmaWindowInterface::setKeepAbove) << QVariant(true); QTest::newRow("keepBelow") << &PlasmaWindow::keepBelowChanged << qVariantFromValue(&PlasmaWindowInterface::setKeepBelow) << QVariant(true); QTest::newRow("maximized") << &PlasmaWindow::maximizedChanged << qVariantFromValue(&PlasmaWindowInterface::setMaximized) << QVariant(true); QTest::newRow("demandsAttention") << &PlasmaWindow::demandsAttentionChanged << qVariantFromValue(&PlasmaWindowInterface::setDemandsAttention) << QVariant(true); QTest::newRow("closeable") << &PlasmaWindow::closeableChanged << qVariantFromValue(&PlasmaWindowInterface::setCloseable) << QVariant(true); QTest::newRow("minimizeable") << &PlasmaWindow::minimizeableChanged << qVariantFromValue(&PlasmaWindowInterface::setMinimizeable) << QVariant(true); QTest::newRow("maximizeable") << &PlasmaWindow::maximizeableChanged << qVariantFromValue(&PlasmaWindowInterface::setMaximizeable) << QVariant(true); QTest::newRow("fullscreenable") << &PlasmaWindow::fullscreenableChanged << qVariantFromValue(&PlasmaWindowInterface::setFullscreenable) << QVariant(true); QTest::newRow("skipTaskbar") << &PlasmaWindow::skipTaskbarChanged << qVariantFromValue(&PlasmaWindowInterface::setSkipTaskbar) << QVariant(true); QTest::newRow("shadeable") << &PlasmaWindow::shadeableChanged << qVariantFromValue(&PlasmaWindowInterface::setShadeable) << QVariant(true); QTest::newRow("shaded") << &PlasmaWindow::shadedChanged << qVariantFromValue(&PlasmaWindowInterface::setShaded) << QVariant(true); QTest::newRow("movable") << &PlasmaWindow::movableChanged << qVariantFromValue(&PlasmaWindowInterface::setMovable) << QVariant(true); QTest::newRow("resizable") << &PlasmaWindow::resizableChanged << qVariantFromValue(&PlasmaWindowInterface::setResizable) << QVariant(true); QTest::newRow("vdChangeable") << &PlasmaWindow::virtualDesktopChangeableChanged << qVariantFromValue(&PlasmaWindowInterface::setVirtualDesktopChangeable) << QVariant(true); QTest::newRow("onallDesktop") << &PlasmaWindow::onAllDesktopsChanged << qVariantFromValue(&PlasmaWindowInterface::setOnAllDesktops) << QVariant(true); QTest::newRow("title") << &PlasmaWindow::titleChanged << qVariantFromValue(&PlasmaWindowInterface::setTitle) << QVariant(QStringLiteral("foo")); QTest::newRow("appId") << &PlasmaWindow::appIdChanged << qVariantFromValue(&PlasmaWindowInterface::setAppId) << QVariant(QStringLiteral("foo")); QTest::newRow("icon" ) << &PlasmaWindow::iconChanged << qVariantFromValue(&PlasmaWindowInterface::setThemedIconName) << QVariant(QStringLiteral("foo")); QTest::newRow("vd") << &PlasmaWindow::virtualDesktopChanged << qVariantFromValue(&PlasmaWindowInterface::setVirtualDesktop) << QVariant(2u); QTest::newRow("unmapped") << &PlasmaWindow::unmapped << qVariantFromValue(&PlasmaWindowInterface::unmap) << QVariant(); } void PlasmaWindowModelTest::testChangeWindowAfterModelDestroy() { // this test verifies that changes in a window after the model got destroyed doesn't crash auto model = m_pw->createWindowModel(); QVERIFY(model); QSignalSpy windowCreatedSpy(m_pw, &PlasmaWindowManagement::windowCreated); QVERIFY(windowCreatedSpy.isValid()); auto w = m_pwInterface->createWindow(m_pwInterface); QVERIFY(windowCreatedSpy.wait()); PlasmaWindow *window = windowCreatedSpy.first().first().value(); // make sure the resource is properly created on server side QCoreApplication::instance()->processEvents(QEventLoop::WaitForMoreEvents); QCOMPARE(model->rowCount(), 1); delete model; QFETCH(ClientWindowSignal, changedSignal); QSignalSpy changedSpy(window, changedSignal); QVERIFY(changedSpy.isValid()); QVERIFY(!window->isActive()); QFETCH(QVariant, setter); QFETCH(QVariant, value); if (QMetaType::Type(value.type()) == QMetaType::Bool) { (w->*(setter.value()))(value.toBool()); } else if (QMetaType::Type(value.type()) == QMetaType::QString) { (w->*(setter.value()))(value.toString()); } else if (QMetaType::Type(value.type()) == QMetaType::UInt) { (w->*(setter.value()))(value.toUInt()); } else if (!value.isValid()) { (w->*(setter.value()))(); } QVERIFY(changedSpy.wait()); } void PlasmaWindowModelTest::testCreateWindowAfterModelDestroy() { // this test verifies that creating a window after the model got destroyed doesn't crash auto model = m_pw->createWindowModel(); QVERIFY(model); delete model; QSignalSpy windowCreatedSpy(m_pw, &PlasmaWindowManagement::windowCreated); QVERIFY(windowCreatedSpy.isValid()); m_pwInterface->createWindow(m_pwInterface); QVERIFY(windowCreatedSpy.wait()); } QTEST_GUILESS_MAIN(PlasmaWindowModelTest) #include "test_plasma_window_model.moc" diff --git a/src/client/plasmawindowmanagement.cpp b/src/client/plasmawindowmanagement.cpp index 430b594..2f95802 100644 --- a/src/client/plasmawindowmanagement.cpp +++ b/src/client/plasmawindowmanagement.cpp @@ -1,1063 +1,1069 @@ /******************************************************************** Copyright 2015 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #include "plasmawindowmanagement.h" #include "plasmawindowmodel.h" #include "plasmavirtualdesktop.h" #include "event_queue.h" #include "output.h" #include "surface.h" #include "wayland_pointer_p.h" // Wayland #include #include #include #include #include #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN PlasmaWindowManagement::Private { public: Private(PlasmaWindowManagement *q); WaylandPointer wm; EventQueue *queue = nullptr; bool showingDesktop = false; QList windows; PlasmaWindow *activeWindow = nullptr; void setup(org_kde_plasma_window_management *wm); private: static void showDesktopCallback(void *data, org_kde_plasma_window_management *org_kde_plasma_window_management, uint32_t state); static void windowCallback(void *data, org_kde_plasma_window_management *org_kde_plasma_window_management, uint32_t id); void setShowDesktop(bool set); void windowCreated(org_kde_plasma_window *id, quint32 internalId); static struct org_kde_plasma_window_management_listener s_listener; PlasmaWindowManagement *q; }; class Q_DECL_HIDDEN PlasmaWindow::Private { public: Private(org_kde_plasma_window *window, quint32 internalId, PlasmaWindow *q); WaylandPointer window; quint32 internalId; QString title; QString appId; quint32 desktop = 0; bool active = false; bool minimized = false; bool maximized = false; bool fullscreen = false; bool keepAbove = false; bool keepBelow = false; bool onAllDesktops = false; bool demandsAttention = false; bool closeable = false; bool minimizeable = false; bool maximizeable = false; bool fullscreenable = false; bool skipTaskbar = false; bool skipSwitcher = false; bool shadeable = false; bool shaded = false; bool movable = false; bool resizable = false; bool virtualDesktopChangeable = false; QIcon icon; PlasmaWindowManagement *wm = nullptr; bool unmapped = false; QPointer parentWindow; QMetaObject::Connection parentWindowUnmappedConnection; QStringList plasmaVirtualDesktops; QRect geometry; quint32 pid = 0; private: static void titleChangedCallback(void *data, org_kde_plasma_window *window, const char *title); static void appIdChangedCallback(void *data, org_kde_plasma_window *window, const char *app_id); static void pidChangedCallback(void *data, org_kde_plasma_window *window, uint32_t pid); static void stateChangedCallback(void *data, org_kde_plasma_window *window, uint32_t state); static void virtualDesktopChangedCallback(void *data, org_kde_plasma_window *window, int32_t number); static void themedIconNameChangedCallback(void *data, org_kde_plasma_window *window, const char *name); static void unmappedCallback(void *data, org_kde_plasma_window *window); static void initialStateCallback(void *data, org_kde_plasma_window *window); static void parentWindowCallback(void *data, org_kde_plasma_window *window, org_kde_plasma_window *parent); static void windowGeometryCallback(void *data, org_kde_plasma_window *window, int32_t x, int32_t y, uint32_t width, uint32_t height); static void iconChangedCallback(void *data, org_kde_plasma_window *org_kde_plasma_window); static void virtualDesktopEnteredCallback(void *data, org_kde_plasma_window *org_kde_plasma_window, const char *id); static void virtualDesktopLeftCallback(void *data, org_kde_plasma_window *org_kde_plasma_window, const char *id); void setActive(bool set); void setMinimized(bool set); void setMaximized(bool set); void setFullscreen(bool set); void setKeepAbove(bool set); void setKeepBelow(bool set); void setOnAllDesktops(bool set); void setDemandsAttention(bool set); void setCloseable(bool set); void setMinimizeable(bool set); void setMaximizeable(bool set); void setFullscreenable(bool set); void setSkipTaskbar(bool skip); void setSkipSwitcher(bool skip); void setShadeable(bool set); void setShaded(bool set); void setMovable(bool set); void setResizable(bool set); void setVirtualDesktopChangeable(bool set); void setParentWindow(PlasmaWindow *parentWindow); void setPid(const quint32 pid); static Private *cast(void *data) { return reinterpret_cast(data); } PlasmaWindow *q; static struct org_kde_plasma_window_listener s_listener; }; PlasmaWindowManagement::Private::Private(PlasmaWindowManagement *q) : q(q) { } org_kde_plasma_window_management_listener PlasmaWindowManagement::Private::s_listener = { showDesktopCallback, windowCallback }; void PlasmaWindowManagement::Private::setup(org_kde_plasma_window_management *windowManagement) { Q_ASSERT(!wm); Q_ASSERT(windowManagement); wm.setup(windowManagement); org_kde_plasma_window_management_add_listener(windowManagement, &s_listener, this); } void PlasmaWindowManagement::Private::showDesktopCallback(void *data, org_kde_plasma_window_management *org_kde_plasma_window_management, uint32_t state) { auto wm = reinterpret_cast(data); Q_ASSERT(wm->wm == org_kde_plasma_window_management); switch (state) { case ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_ENABLED: wm->setShowDesktop(true); break; case ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_DISABLED: wm->setShowDesktop(false); break; default: Q_UNREACHABLE(); break; } } void PlasmaWindowManagement::Private::setShowDesktop(bool set) { if (showingDesktop == set) { return; } showingDesktop = set; emit q->showingDesktopChanged(showingDesktop); } void PlasmaWindowManagement::Private::windowCallback(void *data, org_kde_plasma_window_management *interface, uint32_t id) { auto wm = reinterpret_cast(data); Q_ASSERT(wm->wm == interface); QTimer *timer = new QTimer(); timer->setSingleShot(true); timer->setInterval(0); QObject::connect(timer, &QTimer::timeout, wm->q, [timer, wm, id] { wm->windowCreated(org_kde_plasma_window_management_get_window(wm->wm, id), id); timer->deleteLater(); }, Qt::QueuedConnection ); timer->start(); } void PlasmaWindowManagement::Private::windowCreated(org_kde_plasma_window *id, quint32 internalId) { if (queue) { queue->addProxy(id); } PlasmaWindow *window = new PlasmaWindow(q, id, internalId); window->d->wm = q; windows << window; QObject::connect(window, &QObject::destroyed, q, [this, window] { windows.removeAll(window); if (activeWindow == window) { activeWindow = nullptr; emit q->activeWindowChanged(); } } ); QObject::connect(window, &PlasmaWindow::unmapped, q, [this, window] { if (activeWindow == window) { activeWindow = nullptr; emit q->activeWindowChanged(); } } ); QObject::connect(window, &PlasmaWindow::activeChanged, q, [this, window] { if (window->isActive()) { if (activeWindow == window) { return; } activeWindow = window; emit q->activeWindowChanged(); } else { if (activeWindow == window) { activeWindow = nullptr; emit q->activeWindowChanged(); } } } ); } PlasmaWindowManagement::PlasmaWindowManagement(QObject *parent) : QObject(parent) , d(new Private(this)) { } PlasmaWindowManagement::~PlasmaWindowManagement() { release(); } void PlasmaWindowManagement::destroy() { if (!d->wm) { return; } emit interfaceAboutToBeDestroyed(); d->wm.destroy(); } void PlasmaWindowManagement::release() { if (!d->wm) { return; } emit interfaceAboutToBeReleased(); d->wm.release(); } void PlasmaWindowManagement::setup(org_kde_plasma_window_management *wm) { d->setup(wm); } void PlasmaWindowManagement::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *PlasmaWindowManagement::eventQueue() { return d->queue; } bool PlasmaWindowManagement::isValid() const { return d->wm.isValid(); } PlasmaWindowManagement::operator org_kde_plasma_window_management*() { return d->wm; } PlasmaWindowManagement::operator org_kde_plasma_window_management*() const { return d->wm; } void PlasmaWindowManagement::hideDesktop() { setShowingDesktop(false); } void PlasmaWindowManagement::showDesktop() { setShowingDesktop(true); } void PlasmaWindowManagement::setShowingDesktop(bool show) { org_kde_plasma_window_management_show_desktop(d->wm, show ? ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_ENABLED : ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_DISABLED); } bool PlasmaWindowManagement::isShowingDesktop() const { return d->showingDesktop; } QList< PlasmaWindow* > PlasmaWindowManagement::windows() const { return d->windows; } PlasmaWindow *PlasmaWindowManagement::activeWindow() const { return d->activeWindow; } PlasmaWindowModel *PlasmaWindowManagement::createWindowModel() { return new PlasmaWindowModel(this); } org_kde_plasma_window_listener PlasmaWindow::Private::s_listener = { titleChangedCallback, appIdChangedCallback, stateChangedCallback, virtualDesktopChangedCallback, themedIconNameChangedCallback, unmappedCallback, initialStateCallback, parentWindowCallback, windowGeometryCallback, iconChangedCallback, pidChangedCallback, virtualDesktopEnteredCallback, virtualDesktopLeftCallback }; void PlasmaWindow::Private::parentWindowCallback(void *data, org_kde_plasma_window *window, org_kde_plasma_window *parent) { Q_UNUSED(window) Private *p = cast(data); const auto windows = p->wm->windows(); auto it = std::find_if(windows.begin(), windows.end(), [parent] (const PlasmaWindow *w) { return *w == parent; } ); p->setParentWindow(it != windows.end() ? *it : nullptr); } void PlasmaWindow::Private::windowGeometryCallback(void *data, org_kde_plasma_window *window, int32_t x, int32_t y, uint32_t width, uint32_t height) { Q_UNUSED(window) Private *p = cast(data); QRect geo(x, y, width, height); if (geo == p->geometry) { return; } p->geometry = geo; emit p->q->geometryChanged(); } void PlasmaWindow::Private::setParentWindow(PlasmaWindow *parent) { const auto old = parentWindow; QObject::disconnect(parentWindowUnmappedConnection); if (parent && !parent->d->unmapped) { parentWindow = QPointer(parent); parentWindowUnmappedConnection = QObject::connect(parent, &PlasmaWindow::unmapped, q, [this] { setParentWindow(nullptr); } ); } else { parentWindow = QPointer(); parentWindowUnmappedConnection = QMetaObject::Connection(); } if (parentWindow.data() != old.data()) { emit q->parentWindowChanged(); } } void PlasmaWindow::Private::initialStateCallback(void *data, org_kde_plasma_window *window) { Q_UNUSED(window) Private *p = cast(data); if (!p->unmapped) { emit p->wm->windowCreated(p->q); } } void PlasmaWindow::Private::titleChangedCallback(void *data, org_kde_plasma_window *window, const char *title) { Q_UNUSED(window) Private *p = cast(data); const QString t = QString::fromUtf8(title); if (p->title == t) { return; } p->title = t; emit p->q->titleChanged(); } void PlasmaWindow::Private::appIdChangedCallback(void *data, org_kde_plasma_window *window, const char *appId) { Q_UNUSED(window) Private *p = cast(data); const QString s = QString::fromUtf8(appId); if (s == p->appId) { return; } p->appId = s; emit p->q->appIdChanged(); } void PlasmaWindow::Private::pidChangedCallback(void *data, org_kde_plasma_window *window, uint32_t pid) { Q_UNUSED(window) Private *p = cast(data); if (p->pid == static_cast(pid)) { return; } p->pid = pid; } void PlasmaWindow::Private::virtualDesktopChangedCallback(void *data, org_kde_plasma_window *window, int32_t number) { Q_UNUSED(window) Private *p = cast(data); if (p->desktop == static_cast(number)) { return; } p->desktop = number; emit p->q->virtualDesktopChanged(); } void PlasmaWindow::Private::unmappedCallback(void *data, org_kde_plasma_window *window) { auto p = cast(data); Q_UNUSED(window); p->unmapped = true; emit p->q->unmapped(); p->q->deleteLater(); } void PlasmaWindow::Private::virtualDesktopEnteredCallback(void *data, org_kde_plasma_window *window, const char *id) { auto p = cast(data); Q_UNUSED(window); const QString stringId(QString::fromUtf8(id)); p->plasmaVirtualDesktops << stringId; emit p->q->plasmaVirtualDesktopEntered(stringId); + if (p->plasmaVirtualDesktops.count() == 1) { + emit p->q->onAllDesktopsChanged(); + } } void PlasmaWindow::Private::virtualDesktopLeftCallback(void *data, org_kde_plasma_window *window, const char *id) { auto p = cast(data); Q_UNUSED(window); const QString stringId(QString::fromUtf8(id)); p->plasmaVirtualDesktops.removeAll(stringId); emit p->q->plasmaVirtualDesktopLeft(stringId); + if (p->plasmaVirtualDesktops.isEmpty()) { + emit p->q->onAllDesktopsChanged(); + } } void PlasmaWindow::Private::stateChangedCallback(void *data, org_kde_plasma_window *window, uint32_t state) { auto p = cast(data); Q_UNUSED(window); p->setActive(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE); p->setMinimized(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED); p->setMaximized(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED); p->setFullscreen(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREEN); p->setKeepAbove(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE); p->setKeepBelow(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW); p->setOnAllDesktops(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ON_ALL_DESKTOPS); p->setDemandsAttention(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_DEMANDS_ATTENTION); p->setCloseable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_CLOSEABLE); p->setFullscreenable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREENABLE); p->setMaximizeable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZABLE); p->setMinimizeable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZABLE); p->setSkipTaskbar(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPTASKBAR); p->setSkipSwitcher(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPSWITCHER); p->setShadeable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADEABLE); p->setShaded(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED); p->setMovable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MOVABLE); p->setResizable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_RESIZABLE); p->setVirtualDesktopChangeable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_VIRTUAL_DESKTOP_CHANGEABLE); } void PlasmaWindow::Private::themedIconNameChangedCallback(void *data, org_kde_plasma_window *window, const char *name) { auto p = cast(data); Q_UNUSED(window); const QString themedName = QString::fromUtf8(name); if (!themedName.isEmpty()) { QIcon icon = QIcon::fromTheme(themedName); p->icon = icon; } else { p->icon = QIcon(); } emit p->q->iconChanged(); } static int readData(int fd, QByteArray &data) { // implementation based on QtWayland file qwaylanddataoffer.cpp char buf[4096]; int retryCount = 0; int n; while (true) { n = QT_READ(fd, buf, sizeof buf); if (n == -1 && (errno == EAGAIN) && ++retryCount < 1000) { usleep(1000); } else { break; } } if (n > 0) { data.append(buf, n); n = readData(fd, data); } return n; } void PlasmaWindow::Private::iconChangedCallback(void *data, org_kde_plasma_window *window) { auto p = cast(data); Q_UNUSED(window); int pipeFds[2]; if (pipe2(pipeFds, O_CLOEXEC|O_NONBLOCK) != 0) { return; } org_kde_plasma_window_get_icon(p->window, pipeFds[1]); close(pipeFds[1]); const int pipeFd = pipeFds[0]; auto readIcon = [pipeFd] () -> QIcon { QByteArray content; if (readData(pipeFd, content) != 0) { close(pipeFd); return QIcon(); } close(pipeFd); QDataStream ds(content); QIcon icon; ds >> icon; return icon; }; QFutureWatcher *watcher = new QFutureWatcher(p->q); QObject::connect(watcher, &QFutureWatcher::finished, p->q, [p, watcher] { watcher->deleteLater(); QIcon icon = watcher->result(); if (!icon.isNull()) { p->icon = icon; } else { p->icon = QIcon::fromTheme(QStringLiteral("wayland")); } emit p->q->iconChanged(); } ); watcher->setFuture(QtConcurrent::run(readIcon)); } void PlasmaWindow::Private::setActive(bool set) { if (active == set) { return; } active = set; emit q->activeChanged(); } void PlasmaWindow::Private::setFullscreen(bool set) { if (fullscreen == set) { return; } fullscreen = set; emit q->fullscreenChanged(); } void PlasmaWindow::Private::setKeepAbove(bool set) { if (keepAbove == set) { return; } keepAbove = set; emit q->keepAboveChanged(); } void PlasmaWindow::Private::setKeepBelow(bool set) { if (keepBelow == set) { return; } keepBelow = set; emit q->keepBelowChanged(); } void PlasmaWindow::Private::setMaximized(bool set) { if (maximized == set) { return; } maximized = set; emit q->maximizedChanged(); } void PlasmaWindow::Private::setMinimized(bool set) { if (minimized == set) { return; } minimized = set; emit q->minimizedChanged(); } void PlasmaWindow::Private::setOnAllDesktops(bool set) { if (onAllDesktops == set) { return; } onAllDesktops = set; emit q->onAllDesktopsChanged(); } void PlasmaWindow::Private::setDemandsAttention(bool set) { if (demandsAttention == set) { return; } demandsAttention = set; emit q->demandsAttentionChanged(); } void PlasmaWindow::Private::setCloseable(bool set) { if (closeable == set) { return; } closeable = set; emit q->closeableChanged(); } void PlasmaWindow::Private::setFullscreenable(bool set) { if (fullscreenable == set) { return; } fullscreenable = set; emit q->fullscreenableChanged(); } void PlasmaWindow::Private::setMaximizeable(bool set) { if (maximizeable == set) { return; } maximizeable = set; emit q->maximizeableChanged(); } void PlasmaWindow::Private::setMinimizeable(bool set) { if (minimizeable == set) { return; } minimizeable = set; emit q->minimizeableChanged(); } void PlasmaWindow::Private::setSkipTaskbar(bool skip) { if (skipTaskbar == skip) { return; } skipTaskbar = skip; emit q->skipTaskbarChanged(); } void PlasmaWindow::Private::setSkipSwitcher(bool skip) { if (skipSwitcher == skip) { return; } skipSwitcher = skip; emit q->skipSwitcherChanged(); } void PlasmaWindow::Private::setShadeable(bool set) { if (shadeable == set) { return; } shadeable = set; emit q->shadeableChanged(); } void PlasmaWindow::Private::setShaded(bool set) { if (shaded == set) { return; } shaded = set; emit q->shadedChanged(); } void PlasmaWindow::Private::setMovable(bool set) { if (movable == set) { return; } movable = set; emit q->movableChanged(); } void PlasmaWindow::Private::setResizable(bool set) { if (resizable == set) { return; } resizable = set; emit q->resizableChanged(); } void PlasmaWindow::Private::setVirtualDesktopChangeable(bool set) { if (virtualDesktopChangeable == set) { return; } virtualDesktopChangeable = set; emit q->virtualDesktopChangeableChanged(); } PlasmaWindow::Private::Private(org_kde_plasma_window *w, quint32 internalId, PlasmaWindow *q) : internalId(internalId) , q(q) { window.setup(w); org_kde_plasma_window_add_listener(w, &s_listener, this); } PlasmaWindow::PlasmaWindow(PlasmaWindowManagement *parent, org_kde_plasma_window *window, quint32 internalId) : QObject(parent) , d(new Private(window, internalId, this)) { } PlasmaWindow::~PlasmaWindow() { release(); } void PlasmaWindow::destroy() { d->window.destroy(); } void PlasmaWindow::release() { d->window.release(); } bool PlasmaWindow::isValid() const { return d->window.isValid(); } PlasmaWindow::operator org_kde_plasma_window*() const { return d->window; } PlasmaWindow::operator org_kde_plasma_window*() { return d->window; } QString PlasmaWindow::appId() const { return d->appId; } quint32 PlasmaWindow::pid() const { return d->pid; } QString PlasmaWindow::title() const { return d->title; } quint32 PlasmaWindow::virtualDesktop() const { return d->desktop; } bool PlasmaWindow::isActive() const { return d->active; } bool PlasmaWindow::isFullscreen() const { return d->fullscreen; } bool PlasmaWindow::isKeepAbove() const { return d->keepAbove; } bool PlasmaWindow::isKeepBelow() const { return d->keepBelow; } bool PlasmaWindow::isMaximized() const { return d->maximized; } bool PlasmaWindow::isMinimized() const { return d->minimized; } bool PlasmaWindow::isOnAllDesktops() const { //from protocol version 8 virtual desktops are managed by plasmaVirtualDesktops if (org_kde_plasma_window_get_version(d->window) < 8) { return d->onAllDesktops; } else { return d->plasmaVirtualDesktops.isEmpty(); } } bool PlasmaWindow::isDemandingAttention() const { return d->demandsAttention; } bool PlasmaWindow::isCloseable() const { return d->closeable; } bool PlasmaWindow::isFullscreenable() const { return d->fullscreenable; } bool PlasmaWindow::isMaximizeable() const { return d->maximizeable; } bool PlasmaWindow::isMinimizeable() const { return d->minimizeable; } bool PlasmaWindow::skipTaskbar() const { return d->skipTaskbar; } bool PlasmaWindow::skipSwitcher() const { return d->skipSwitcher; } QIcon PlasmaWindow::icon() const { return d->icon; } bool PlasmaWindow::isShadeable() const { return d->shadeable; } bool PlasmaWindow::isShaded() const { return d->shaded; } bool PlasmaWindow::isResizable() const { return d->resizable; } bool PlasmaWindow::isMovable() const { return d->movable; } bool PlasmaWindow::isVirtualDesktopChangeable() const { return d->virtualDesktopChangeable; } void PlasmaWindow::requestActivate() { org_kde_plasma_window_set_state(d->window, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE); } void PlasmaWindow::requestClose() { org_kde_plasma_window_close(d->window); } void PlasmaWindow::requestMove() { org_kde_plasma_window_request_move(d->window); } void PlasmaWindow::requestResize() { org_kde_plasma_window_request_resize(d->window); } void PlasmaWindow::requestVirtualDesktop(quint32 desktop) { org_kde_plasma_window_set_virtual_desktop(d->window, desktop); } void PlasmaWindow::requestToggleKeepAbove() { if (d->keepAbove) { org_kde_plasma_window_set_state(d->window, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE, 0); } else { org_kde_plasma_window_set_state(d->window, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE); } } void PlasmaWindow::requestToggleKeepBelow() { if (d->keepBelow) { org_kde_plasma_window_set_state(d->window, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW, 0); } else { org_kde_plasma_window_set_state(d->window, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW); } } void PlasmaWindow::requestToggleMinimized() { if (d->minimized) { org_kde_plasma_window_set_state(d->window, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED, 0); } else { org_kde_plasma_window_set_state(d->window, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED); } } void PlasmaWindow::requestToggleMaximized() { if (d->maximized) { org_kde_plasma_window_set_state(d->window, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED, 0); } else { org_kde_plasma_window_set_state(d->window, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED); } } void PlasmaWindow::setMinimizedGeometry(Surface *panel, const QRect &geom) { org_kde_plasma_window_set_minimized_geometry(d->window, *panel, geom.x(), geom.y(), geom.width(), geom.height()); } void PlasmaWindow::unsetMinimizedGeometry(Surface *panel) { org_kde_plasma_window_unset_minimized_geometry(d->window, *panel); } void PlasmaWindow::requestToggleShaded() { if (d->shaded) { org_kde_plasma_window_set_state(d->window, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED, 0); } else { org_kde_plasma_window_set_state(d->window, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED); } } quint32 PlasmaWindow::internalId() const { return d->internalId; } QPointer PlasmaWindow::parentWindow() const { return d->parentWindow; } QRect PlasmaWindow::geometry() const { return d->geometry; } void PlasmaWindow::requestEnterVirtualDesktop(const QString &id) { org_kde_plasma_window_request_enter_virtual_desktop(d->window, id.toUtf8()); } void PlasmaWindow::requestEnterNewVirtualDesktop() { org_kde_plasma_window_request_enter_new_virtual_desktop(d->window); } void PlasmaWindow::requestLeaveVirtualDesktop(const QString &id) { org_kde_plasma_window_request_leave_virtual_desktop(d->window, id.toUtf8()); } QStringList PlasmaWindow::plasmaVirtualDesktops() const { return d->plasmaVirtualDesktops; } } } diff --git a/src/client/plasmawindowmodel.cpp b/src/client/plasmawindowmodel.cpp index 4ef7d85..830f33b 100644 --- a/src/client/plasmawindowmodel.cpp +++ b/src/client/plasmawindowmodel.cpp @@ -1,369 +1,379 @@ /******************************************************************** Copyright 2015 Eike Hein This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #include "plasmawindowmodel.h" #include "plasmawindowmanagement.h" #include namespace KWayland { namespace Client { class Q_DECL_HIDDEN PlasmaWindowModel::Private { public: Private(PlasmaWindowModel *q); QList windows; PlasmaWindow *window = nullptr; void addWindow(PlasmaWindow *window); void dataChanged(PlasmaWindow *window, int role); private: PlasmaWindowModel *q; }; PlasmaWindowModel::Private::Private(PlasmaWindowModel *q) : q(q) { } void PlasmaWindowModel::Private::addWindow(PlasmaWindow *window) { if (windows.indexOf(window) != -1) { return; } const int count = windows.count(); q->beginInsertRows(QModelIndex(), count, count); windows.append(window); q->endInsertRows(); auto removeWindow = [window, this] { const int row = windows.indexOf(window); if (row != -1) { q->beginRemoveRows(QModelIndex(), row, row); windows.removeAt(row); q->endRemoveRows(); } }; QObject::connect(window, &PlasmaWindow::unmapped, q, removeWindow); QObject::connect(window, &QObject::destroyed, q, removeWindow); QObject::connect(window, &PlasmaWindow::titleChanged, q, [window, this] { this->dataChanged(window, Qt::DisplayRole); } ); QObject::connect(window, &PlasmaWindow::iconChanged, q, [window, this] { this->dataChanged(window, Qt::DecorationRole); } ); QObject::connect(window, &PlasmaWindow::appIdChanged, q, [window, this] { this->dataChanged(window, PlasmaWindowModel::AppId); } ); QObject::connect(window, &PlasmaWindow::activeChanged, q, [window, this] { this->dataChanged(window, IsActive); } ); QObject::connect(window, &PlasmaWindow::fullscreenableChanged, q, [window, this] { this->dataChanged(window, IsFullscreenable); } ); QObject::connect(window, &PlasmaWindow::fullscreenChanged, q, [window, this] { this->dataChanged(window, IsFullscreen); } ); QObject::connect(window, &PlasmaWindow::maximizeableChanged, q, [window, this] { this->dataChanged(window, IsMaximizable); } ); QObject::connect(window, &PlasmaWindow::maximizedChanged, q, [window, this] { this->dataChanged(window, IsMaximized); } ); QObject::connect(window, &PlasmaWindow::minimizeableChanged, q, [window, this] { this->dataChanged(window, IsMinimizable); } ); QObject::connect(window, &PlasmaWindow::minimizedChanged, q, [window, this] { this->dataChanged(window, IsMinimized); } ); QObject::connect(window, &PlasmaWindow::keepAboveChanged, q, [window, this] { this->dataChanged(window, IsKeepAbove); } ); QObject::connect(window, &PlasmaWindow::keepBelowChanged, q, [window, this] { this->dataChanged(window, IsKeepBelow); } ); QObject::connect(window, &PlasmaWindow::virtualDesktopChanged, q, [window, this] { this->dataChanged(window, VirtualDesktop); } ); QObject::connect(window, &PlasmaWindow::onAllDesktopsChanged, q, [window, this] { this->dataChanged(window, IsOnAllDesktops); } ); QObject::connect(window, &PlasmaWindow::demandsAttentionChanged, q, [window, this] { this->dataChanged(window, IsDemandingAttention); } ); QObject::connect(window, &PlasmaWindow::skipTaskbarChanged, q, [window, this] { this->dataChanged(window, SkipTaskbar); } ); QObject::connect(window, &PlasmaWindow::skipSwitcherChanged, q, [window, this] { this->dataChanged(window, SkipSwitcher); } ); QObject::connect(window, &PlasmaWindow::shadeableChanged, q, [window, this] { this->dataChanged(window, IsShadeable); } ); QObject::connect(window, &PlasmaWindow::shadedChanged, q, [window, this] { this->dataChanged(window, IsShaded); } ); QObject::connect(window, &PlasmaWindow::movableChanged, q, [window, this] { this->dataChanged(window, IsMovable); } ); QObject::connect(window, &PlasmaWindow::resizableChanged, q, [window, this] { this->dataChanged(window, IsResizable); } ); QObject::connect(window, &PlasmaWindow::virtualDesktopChangeableChanged, q, [window, this] { this->dataChanged(window, IsVirtualDesktopChangeable); } ); QObject::connect(window, &PlasmaWindow::closeableChanged, q, [window, this] { this->dataChanged(window, IsCloseable); } ); QObject::connect(window, &PlasmaWindow::geometryChanged, q, [window, this] { this->dataChanged(window, Geometry); } ); + + QObject::connect(window, &PlasmaWindow::plasmaVirtualDesktopEntered, q, + [window, this] { this->dataChanged(window, VirtualDesktops); } + ); + + QObject::connect(window, &PlasmaWindow::plasmaVirtualDesktopLeft, q, + [window, this] { this->dataChanged(window, VirtualDesktops); } + ); } void PlasmaWindowModel::Private::dataChanged(PlasmaWindow *window, int role) { QModelIndex idx = q->index(windows.indexOf(window)); emit q->dataChanged(idx, idx, QVector() << role); } PlasmaWindowModel::PlasmaWindowModel(PlasmaWindowManagement *parent) : QAbstractListModel(parent) , d(new Private(this)) { connect(parent, &PlasmaWindowManagement::interfaceAboutToBeReleased, this, [this] { beginResetModel(); d->windows.clear(); endResetModel(); } ); connect(parent, &PlasmaWindowManagement::windowCreated, this, [this](PlasmaWindow *window) { d->addWindow(window); } ); for (auto it = parent->windows().constBegin(); it != parent->windows().constEnd(); ++it) { d->addWindow(*it); } } PlasmaWindowModel::~PlasmaWindowModel() { } QHash PlasmaWindowModel::roleNames() const { QHash roles; roles.insert(Qt::DisplayRole, "DisplayRole"); roles.insert(Qt::DecorationRole, "DecorationRole"); QMetaEnum e = metaObject()->enumerator(metaObject()->indexOfEnumerator("AdditionalRoles")); for (int i = 0; i < e.keyCount(); ++i) { roles.insert(e.value(i), e.key(i)); } return roles; } QVariant PlasmaWindowModel::data(const QModelIndex &index, int role) const { if (!index.isValid() || index.row() >= d->windows.count()) { return QVariant(); } const PlasmaWindow *window = d->windows.at(index.row()); if (role == Qt::DisplayRole) { return window->title(); } else if (role == Qt::DecorationRole) { return window->icon(); } else if (role == AppId) { return window->appId(); } else if (role == Pid) { return window->pid(); } else if (role == IsActive) { return window->isActive(); } else if (role == IsFullscreenable) { return window->isFullscreenable(); } else if (role == IsFullscreen) { return window->isFullscreen(); } else if (role == IsMaximizable) { return window->isMaximizeable(); } else if (role == IsMaximized) { return window->isMaximized(); } else if (role == IsMinimizable) { return window->isMinimizeable(); } else if (role == IsMinimized) { return window->isMinimized(); } else if (role == IsKeepAbove) { return window->isKeepAbove(); } else if (role == IsKeepBelow) { return window->isKeepBelow(); } else if (role == VirtualDesktop) { return window->virtualDesktop(); } else if (role == IsOnAllDesktops) { return window->isOnAllDesktops(); } else if (role == IsDemandingAttention) { return window->isDemandingAttention(); } else if (role == SkipTaskbar) { return window->skipTaskbar(); } else if (role == SkipSwitcher) { return window->skipSwitcher(); } else if (role == IsShadeable) { return window->isShadeable(); } else if (role == IsShaded) { return window->isShaded(); } else if (role == IsMovable) { return window->isMovable(); } else if (role == IsResizable) { return window->isResizable(); } else if (role == IsVirtualDesktopChangeable) { return window->isVirtualDesktopChangeable(); } else if (role == IsCloseable) { return window->isCloseable(); } else if (role == Geometry) { return window->geometry(); + } else if (role == VirtualDesktops) { + return window->plasmaVirtualDesktops(); } return QVariant(); } int PlasmaWindowModel::rowCount(const QModelIndex &parent) const { return parent.isValid() ? 0 : d->windows.count(); } QModelIndex PlasmaWindowModel::index(int row, int column, const QModelIndex &parent) const { return hasIndex(row, column, parent) ? createIndex(row, column, d->windows.at(row)) : QModelIndex(); } Q_INVOKABLE void PlasmaWindowModel::requestActivate(int row) { if (row >= 0 && row < d->windows.count()) { d->windows.at(row)->requestActivate(); } } Q_INVOKABLE void PlasmaWindowModel::requestClose(int row) { if (row >= 0 && row < d->windows.count()) { d->windows.at(row)->requestClose(); } } Q_INVOKABLE void PlasmaWindowModel::requestMove(int row) { if (row >= 0 && row < d->windows.count()) { d->windows.at(row)->requestMove(); } } Q_INVOKABLE void PlasmaWindowModel::requestResize(int row) { if (row >= 0 && row < d->windows.count()) { d->windows.at(row)->requestResize(); } } Q_INVOKABLE void PlasmaWindowModel::requestVirtualDesktop(int row, quint32 desktop) { if (row >= 0 && row < d->windows.count()) { d->windows.at(row)->requestVirtualDesktop(desktop); } } Q_INVOKABLE void PlasmaWindowModel::requestToggleKeepAbove(int row) { if (row >= 0 && row < d->windows.count()) { d->windows.at(row)->requestToggleKeepAbove(); } } Q_INVOKABLE void PlasmaWindowModel::requestToggleKeepBelow(int row) { if (row >= 0 && row < d->windows.count()) { d->windows.at(row)->requestToggleKeepBelow(); } } Q_INVOKABLE void PlasmaWindowModel::requestToggleMinimized(int row) { if (row >= 0 && row < d->windows.count()) { d->windows.at(row)->requestToggleMinimized(); } } Q_INVOKABLE void PlasmaWindowModel::requestToggleMaximized(int row) { if (row >= 0 && row < d->windows.count()) { d->windows.at(row)->requestToggleMaximized(); } } Q_INVOKABLE void PlasmaWindowModel::setMinimizedGeometry(int row, Surface *panel, const QRect &geom) { if (row >= 0 && row < d->windows.count()) { d->windows.at(row)->setMinimizedGeometry(panel, geom); } } Q_INVOKABLE void PlasmaWindowModel::requestToggleShaded(int row) { if (row >= 0 && row < d->windows.count()) { d->windows.at(row)->requestToggleShaded(); } } } } diff --git a/src/client/plasmawindowmodel.h b/src/client/plasmawindowmodel.h index ae8d69e..be70605 100644 --- a/src/client/plasmawindowmodel.h +++ b/src/client/plasmawindowmodel.h @@ -1,200 +1,207 @@ /******************************************************************** Copyright 2015 Eike Hein This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #ifndef WAYLAND_PLASMAWINDOWMODEL_H #define WAYLAND_PLASMAWINDOWMODEL_H #include #include namespace KWayland { namespace Client { class PlasmaWindowManagement; class Surface; /** * @short Exposes the window list and window state as a Qt item model. * * This class is a QAbstractListModel implementation that exposes information * from a PlasmaWindowManagement instance passed as parent and enables convenient * calls to PlasmaWindow methods through a model row index. * * The model is destroyed when the PlasmaWindowManagement parent is. * * The model resets when the PlasmaWindowManagement parent signals that its * interface is about to be destroyed. * * To use this class you can create an instance yourself, or preferably use the * convenience method in PlasmaWindowManagement: * @code * PlasmaWindowModel *model = wm->createWindowModel(); * @endcode * * @see PlasmaWindowManagement * @see PlasmaWindow **/ class KWAYLANDCLIENT_EXPORT PlasmaWindowModel : public QAbstractListModel { Q_OBJECT public: enum AdditionalRoles { AppId = Qt::UserRole + 1, IsActive, IsFullscreenable, IsFullscreen, IsMaximizable, IsMaximized, IsMinimizable, IsMinimized, IsKeepAbove, IsKeepBelow, + /** + @deprecated @see VirtualDesktops + */ VirtualDesktop, IsOnAllDesktops, IsDemandingAttention, SkipTaskbar, /** * @since 5.22 */ IsShadeable, /** * @since 5.22 */ IsShaded, /** * @since 5.22 */ IsMovable, /** * @since 5.22 */ IsResizable, /** * @since 5.22 */ IsVirtualDesktopChangeable, /** * @since 5.22 */ IsCloseable, /** * @since 5.25 */ Geometry, /** * @since 5.35 */ Pid, /** * @since 5.47 */ SkipSwitcher, + /** + * @since 5.55 + */ + VirtualDesktops }; Q_ENUM(AdditionalRoles) explicit PlasmaWindowModel(PlasmaWindowManagement *parent); virtual ~PlasmaWindowModel(); QHash roleNames() const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; /** * Returns an index with internalPointer() pointing to a PlasmaWindow instance. **/ QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const override; /** * Request the window at this model row index be activated. **/ Q_INVOKABLE void requestActivate(int row); /** * Request the window at this model row index be closed. **/ Q_INVOKABLE void requestClose(int row); /** * Request an interactive move for the window at this model row index. * @since 5.22 **/ Q_INVOKABLE void requestMove(int row); /** * Request an interactive resize for the window at this model row index. * @since 5.22 **/ Q_INVOKABLE void requestResize(int row); /** * Request the window at this model row index be moved to this virtual desktop. **/ Q_INVOKABLE void requestVirtualDesktop(int row, quint32 desktop); /** * Requests the window at this model row index have its keep above state toggled. * @since 5.35 */ Q_INVOKABLE void requestToggleKeepAbove(int row); /** * Requests the window at this model row index have its keep above state toggled. * @since 5.35 */ Q_INVOKABLE void requestToggleKeepBelow(int row); /** * Requests the window at this model row index have its minimized state toggled. */ Q_INVOKABLE void requestToggleMinimized(int row); /** * Requests the window at this model row index have its maximized state toggled. */ Q_INVOKABLE void requestToggleMaximized(int row); /** * Sets the geometry of the taskbar entry for the window at the model row * relative to a panel in particular. QRectF, intended for use from QML * @since 5.5 */ Q_INVOKABLE void setMinimizedGeometry(int row, Surface *panel, const QRect &geom); /** * Requests the window at this model row index have its shaded state toggled. * @since 5.22 */ Q_INVOKABLE void requestToggleShaded(int row); private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/server/plasmawindowmanagement_interface.cpp b/src/server/plasmawindowmanagement_interface.cpp index b1e583b..d7f665d 100644 --- a/src/server/plasmawindowmanagement_interface.cpp +++ b/src/server/plasmawindowmanagement_interface.cpp @@ -1,960 +1,954 @@ /******************************************************************** Copyright 2015 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #include "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); 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; 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 = 9; PlasmaWindowManagementInterface::Private::Private(PlasmaWindowManagementInterface *q, Display *d) : Global::Private(d, &org_kde_plasma_window_management_interface, s_version) , q(q) { } #ifndef DOXYGEN_SHOULD_SKIP_THIS 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 DOXYGEN_SHOULD_SKIP_THIS 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()); } 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::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); } #ifndef KWAYLANDSERVER_NO_DEPRECATED void PlasmaWindowInterface::setVirtualDesktop(quint32 desktop) { d->setVirtualDesktop(desktop); } #endif 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); } #ifndef KWAYLANDSERVER_NO_DEPRECATED void PlasmaWindowInterface::setThemedIconName(const QString &iconName) { d->setThemedIconName(iconName); } #endif 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; } - //full? lets set it on all desktops, the plasmaVirtualDesktops list will get empty, which means it's on all desktops - if (d->wm->plasmaVirtualDesktopManagementInterface()->desktops().count() == d->plasmaVirtualDesktops.count() + 1) { - setOnAllDesktops(true); - 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); } } }