diff --git a/autotests/client/CMakeLists.txt b/autotests/client/CMakeLists.txt --- a/autotests/client/CMakeLists.txt +++ b/autotests/client/CMakeLists.txt @@ -429,3 +429,14 @@ target_link_libraries( testRemoteAccess Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer) add_test(NAME kwayland-testRemoteAccess COMMAND testRemoteAccess) ecm_mark_as_test(testRemoteAccess) + +######################################################## +# Test VirtualDesktop +######################################################## +set( testPlasmaVirtualDesktop_SRCS + test_plasma_virtual_desktop.cpp + ) +add_executable(testPlasmaVirtualDesktop ${testPlasmaVirtualDesktop_SRCS}) +target_link_libraries( testPlasmaVirtualDesktop Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer) +add_test(NAME kwayland-testPlasmaVirtualDesktop COMMAND testPlasmaVirtualDesktop) +ecm_mark_as_test(testPlasmaVirtualDesktop) diff --git a/autotests/client/test_plasma_virtual_desktop.cpp b/autotests/client/test_plasma_virtual_desktop.cpp new file mode 100644 --- /dev/null +++ b/autotests/client/test_plasma_virtual_desktop.cpp @@ -0,0 +1,415 @@ +/******************************************************************** +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 testDestroy(); + void testActivate(); + + void testEnterLeaveDesktop(); + +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_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 desktopAddedSpy(m_plasmaVirtualDesktopManagement, &PlasmaVirtualDesktopManagement::desktopAdded); + 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"); + + desktopAddedSpy.wait(); + QList arguments = desktopAddedSpy.takeFirst(); + QCOMPARE(arguments.at(0).toString(), QStringLiteral("0-1")); + QCOMPARE(arguments.at(1).toUInt(), 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"); + desktopAddedSpy.wait(); + arguments = desktopAddedSpy.takeFirst(); + QCOMPARE(arguments.at(0).toString(), QStringLiteral("0-2")); + QCOMPARE(arguments.at(1).toUInt(), 1); + QCOMPARE(m_plasmaVirtualDesktopManagement->desktops().length(), 2); + + KWayland::Server::PlasmaVirtualDesktopInterface *desktop3Int = m_plasmaVirtualDesktopManagementInterface->createDesktop(QStringLiteral("0-3")); + desktop3Int->setName("Desktop 3"); + desktopAddedSpy.wait(); + arguments = desktopAddedSpy.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::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 0-2 is active + QVERIFY(m_plasmaVirtualDesktopManagement->desktops().first()->active()); + QVERIFY(m_plasmaVirtualDesktopManagementInterface->desktops().first()->active()); + + //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); + + //Test 0-3 is active + QVERIFY(m_plasmaVirtualDesktopManagement->desktops().first()->active()); + QVERIFY(m_plasmaVirtualDesktopManagementInterface->desktops().first()->active()); +} + +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->active()); + QVERIFY(desktop1Int->active()); + + KWayland::Server::PlasmaVirtualDesktopInterface *desktop2Int = m_plasmaVirtualDesktopManagementInterface->desktops()[1]; + KWayland::Client::PlasmaVirtualDesktop *desktop2 = m_plasmaVirtualDesktopManagement->desktops()[1]; + QVERIFY(!desktop2Int->active()); + + QSignalSpy requestActivateSpy(desktop2Int, &KWayland::Server::PlasmaVirtualDesktopInterface::activateRequested); + QSignalSpy activatedSpy(desktop2, &KWayland::Client::PlasmaVirtualDesktop::activated); + + desktop2->requestActivate(); + requestActivateSpy.wait(); + + //activate the desktop that was requested active + m_plasmaVirtualDesktopManagementInterface->setActiveDesktop(desktop2->id()); + activatedSpy.wait(); + + //correct state in the server + QVERIFY(desktop2Int->active()); + QVERIFY(!desktop1Int->active()); + //correct state in the client + QVERIFY(desktop2Int->active()); + QVERIFY(!desktop1Int->active()); + + //Test the deactivated signal + QSignalSpy deactivatedSpy(desktop2, &KWayland::Client::PlasmaVirtualDesktop::deactivated); + m_plasmaVirtualDesktopManagementInterface->setActiveDesktop(desktop1->id()); + 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 1 + 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); +} + +QTEST_GUILESS_MAIN(TestVirtualDesktop) +#include "test_plasma_virtual_desktop.moc" diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -34,6 +34,7 @@ pointerconstraints.cpp pointergestures.cpp plasmashell.cpp + plasmavirtualdesktop.cpp plasmawindowmanagement.cpp plasmawindowmodel.cpp region.cpp @@ -78,6 +79,12 @@ PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/plasma-shell.xml BASENAME plasma-shell ) + +ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS + PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/plasma-virtual-desktop.xml + BASENAME plasma-virtual-desktop +) + ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/plasma-window-management.xml BASENAME plasma-window-management @@ -171,6 +178,7 @@ ${CMAKE_CURRENT_BINARY_DIR}/wayland-output-management-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-org_kde_kwin_outputdevice-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-shell-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-shell-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-window-management-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-idle-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-fake-input-client-protocol.h @@ -250,6 +258,7 @@ pointer.h pointerconstraints.h plasmashell.h + plasmavirtualdesktop.h plasmawindowmanagement.h plasmawindowmodel.h pointergestures.h diff --git a/src/client/plasmavirtualdesktop.h b/src/client/plasmavirtualdesktop.h new file mode 100644 --- /dev/null +++ b/src/client/plasmavirtualdesktop.h @@ -0,0 +1,267 @@ +/**************************************************************************** +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 . +****************************************************************************/ +#ifndef KWAYLAND_CLIENT_PLASMAVIRTUALDESKTOP_H +#define KWAYLAND_CLIENT_PLASMAVIRTUALDESKTOP_H + +#include + +#include + +struct org_kde_plasma_virtual_desktop_management; +struct org_kde_plasma_virtual_desktop; + +namespace KWayland +{ +namespace Client +{ + +class EventQueue; +class PlasmaVirtualDesktop; + +/** + * @short Wrapper for the org_kde_plasma_virtual_desktop_management interface. + * + * This class provides a convenient wrapper for the org_kde_plasma_virtual_desktop_management interface. + * + * To use this class one needs to interact with the Registry. There are two + * possible ways to create the PlasmaVirtualDesktopManagement interface: + * @code + * PlasmaVirtualDesktopManagement *c = registry->createPlasmaVirtualDesktopManagement(name, version); + * @endcode + * + * This creates the PlasmaVirtualDesktopManagement and sets it up directly. As an alternative this + * can also be done in a more low level way: + * @code + * PlasmaVirtualDesktopManagement *c = new PlasmaVirtualDesktopManagement; + * c->setup(registry->bindPlasmaVirtualDesktopManagement(name, version)); + * @endcode + * + * The PlasmaVirtualDesktopManagement can be used as a drop-in replacement for any org_kde_plasma_virtual_desktop_management + * pointer as it provides matching cast operators. + * @since 5.46 + * + * @see Registry + **/ +class KWAYLANDCLIENT_EXPORT PlasmaVirtualDesktopManagement : public QObject +{ + Q_OBJECT +public: + /** + * Creates a new PlasmaVirtualDesktopManagement. + * Note: after constructing the PlasmaVirtualDesktopManagement it is not yet valid and one needs + * to call setup. In order to get a ready to use PlasmaVirtualDesktopManagement prefer using + * Registry::createPlasmaVirtualDesktopManagement. + **/ + explicit PlasmaVirtualDesktopManagement(QObject *parent = nullptr); + virtual ~PlasmaVirtualDesktopManagement(); + + /** + * Setup this PlasmaVirtualDesktopManagement to manage the @p plasmavirtualdesktopmanagement. + * When using Registry::createPlasmaVirtualDesktopManagement there is no need to call this + * method. + **/ + void setup(org_kde_plasma_virtual_desktop_management *plasmavirtualdesktopmanagement); + /** + * @returns @c true if managing a org_kde_plasma_virtual_desktop_management. + **/ + bool isValid() const; + /** + * Releases the org_kde_plasma_virtual_desktop_management interface. + * After the interface has been released the PlasmaVirtualDesktopManagement instance is no + * longer valid and can be setup with another org_kde_plasma_virtual_desktop_management interface. + **/ + void release(); + /** + * Destroys the data held by this PlasmaVirtualDesktopManagement. + * This method is supposed to be used when the connection to the Wayland + * server goes away. If the connection is not valid anymore, it's not + * possible to call release anymore as that calls into the Wayland + * connection and the call would fail. This method cleans up the data, so + * that the instance can be deleted or set up to a new org_kde_plasma_virtual_desktop_management interface + * once there is a new connection available. + * + * It is suggested to connect this method to ConnectionThread::connectionDied: + * @code + * connect(connection, &ConnectionThread::connectionDied, plasmavirtualdesktopmanagement, &PlasmaVirtualDesktopManagement::destroy); + * @endcode + * + * @see release + **/ + void destroy(); + + /** + * Sets the @p queue to use for creating objects with this PlasmaVirtualDesktopManagement. + **/ + void setEventQueue(EventQueue *queue); + + /** + * @returns The event queue to use for creating objects with this PlasmaVirtualDesktopManagement. + * The object is owned by the manager and the caller should not delete it. + **/ + EventQueue *eventQueue(); + + PlasmaVirtualDesktop *getVirtualDesktop(const QString &id); + + /** + * @returns All the existent virtual desktops + */ + QList desktops() const; + + operator org_kde_plasma_virtual_desktop_management*(); + operator org_kde_plasma_virtual_desktop_management*() const; + +Q_SIGNALS: + void removed(); + + /** + * Emitted when a new desktop has been added + */ + void desktopAdded(const QString &id, quint32 position); + + /** + * Emitted when a desktop has been removed + */ + void desktopRemoved(const QString &id); + + /** + * This event is sent after all other properties has been + * sent after binding to the desktop manager object and after any + * other property changes done after that. This allows + * changes to the org_kde_plasma_virtual_desktop_management properties + * to be seen as atomic, even if they happen via multiple events. + */ + void done(); + +private: + class Private; + QScopedPointer d; +}; + +class KWAYLANDCLIENT_EXPORT PlasmaVirtualDesktop : public QObject +{ + Q_OBJECT +public: + virtual ~PlasmaVirtualDesktop(); + + /** + * Setup this PlasmaVirtualDesktop to manage the @p plasmavirtualdesktop. + * When using PlasmaVirtualDesktopManagement::createPlasmaVirtualDesktop there is no need to call this + * method. + **/ + void setup(org_kde_plasma_virtual_desktop *plasmavirtualdesktop); + + /** + * @returns @c true if managing a org_kde_plasma_virtual_desktop. + **/ + bool isValid() const; + + /** + * Releases the org_kde_plasma_virtual_desktop interface. + * After the interface has been released the PlasmaVirtualDesktop instance is no + * longer valid and can be setup with another org_kde_plasma_virtual_desktop interface. + **/ + void release(); + + /** + * Destroys the data held by this PlasmaVirtualDesktop. + * This method is supposed to be used when the connection to the Wayland + * server goes away. If the connection is not valid anymore, it's not + * possible to call release anymore as that calls into the Wayland + * connection and the call would fail. This method cleans up the data, so + * that the instance can be deleted or set up to a new org_kde_plasma_virtual_desktop interface + * once there is a new connection available. + * + * It is suggested to connect this method to ConnectionThread::connectionDied: + * @code + * connect(connection, &ConnectionThread::connectionDied, plasmavirtualdesktop, &PlasmaVirtualDesktop::destroy); + * @endcode + * + * @see release + **/ + void destroy(); + + /** + * Requests this desktop to be activated. + * The server may or may not decide to consent to the request. + */ + void requestActivate(); + + /** + * @returns The unique id of this desktop. The format of the id is decided by the compositor + */ + QString id() const; + + + /** + * @returns User readable name for the desktop. + */ + QString name() const; + + /** + * @returns True if the desktop is the active one. + * when this property changes, activated or deactivated will be emitted. + * @see activated + * @see deactivated + */ + bool active() const; + + operator org_kde_plasma_virtual_desktop*(); + operator org_kde_plasma_virtual_desktop*() const; + +Q_SIGNALS: + /** + * TODO: activeChanged(bool)? + * Emitted when this desktop has been activated by the server + */ + void activated(); + + /** + * Emitted when this desktop has been activated by the server + */ + void deactivated(); + + /** + * This event is sent after all other properties has been + * sent after binding to the desktop manager object and after any + * other property changes done after that. This allows + * changes to the org_kde_plasma_virtual_desktop properties + * to be seen as atomic, even if they happen via multiple events. + */ + void done(); + + /** + * This virtual desktop has just been removed by the server: + * This object itself is about to be deleted. All windows will + * lose the association to this desktop. + */ + void removed(); + +private: + friend class PlasmaVirtualDesktopManagement; + explicit PlasmaVirtualDesktop(QObject *parent = nullptr); + class Private; + QScopedPointer d; +}; + + +} +} + +#endif diff --git a/src/client/plasmavirtualdesktop.cpp b/src/client/plasmavirtualdesktop.cpp new file mode 100644 --- /dev/null +++ b/src/client/plasmavirtualdesktop.cpp @@ -0,0 +1,363 @@ +/**************************************************************************** +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 . +****************************************************************************/ +#include "plasmavirtualdesktop.h" +#include "event_queue.h" +#include "wayland_pointer_p.h" + +#include +#include + +#include + +namespace KWayland +{ +namespace Client +{ + +class Q_DECL_HIDDEN PlasmaVirtualDesktopManagement::Private +{ +public: + Private(PlasmaVirtualDesktopManagement *q); + + void setup(org_kde_plasma_virtual_desktop_management *arg); + + WaylandPointer plasmavirtualdesktopmanagement; + EventQueue *queue = nullptr; + + QList desktops; + + inline QList::const_iterator constFindDesktop(const QString &id); + inline QList::iterator findDesktop(const QString &id); + +private: + static void addedCallback(void *data, org_kde_plasma_virtual_desktop_management *org_kde_plasma_virtual_desktop_management, const char *id, uint32_t position); + static void removedCallback(void *data, org_kde_plasma_virtual_desktop_management *org_kde_plasma_virtual_desktop_management, const char *id); + static void doneCallback(void *data, org_kde_plasma_virtual_desktop_management *org_kde_plasma_virtual_desktop_management); + + PlasmaVirtualDesktopManagement *q; + + static const org_kde_plasma_virtual_desktop_management_listener s_listener; +}; + +inline QList::const_iterator PlasmaVirtualDesktopManagement::Private::constFindDesktop(const QString &id) +{ + return std::find_if( desktops.constBegin(), + desktops.constEnd(), + [id]( const PlasmaVirtualDesktop *desk ){ return desk->id() == id; } ); +} + +inline QList::iterator PlasmaVirtualDesktopManagement::Private::findDesktop(const QString &id) +{ + return std::find_if( desktops.begin(), + desktops.end(), + [id]( const PlasmaVirtualDesktop *desk ){ return desk->id() == id; } ); +} + +const org_kde_plasma_virtual_desktop_management_listener PlasmaVirtualDesktopManagement::Private::s_listener = { + addedCallback, + removedCallback, + doneCallback +}; + +void PlasmaVirtualDesktopManagement::Private::addedCallback(void *data, org_kde_plasma_virtual_desktop_management *org_kde_plasma_virtual_desktop_management, const char *id, uint32_t position) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->plasmavirtualdesktopmanagement == org_kde_plasma_virtual_desktop_management); + const QString stringId = QString::fromUtf8(id); + PlasmaVirtualDesktop *vd = p->q->getVirtualDesktop(stringId); + Q_ASSERT(vd); + + p->desktops.insert(position, vd); + //TODO: emit a lot of desktopMoved? + + emit p->q->desktopAdded(stringId, position); +} + +void PlasmaVirtualDesktopManagement::Private::removedCallback(void *data, org_kde_plasma_virtual_desktop_management *org_kde_plasma_virtual_desktop_management, const char *id) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->plasmavirtualdesktopmanagement == org_kde_plasma_virtual_desktop_management); + const QString stringId = QString::fromUtf8(id); + PlasmaVirtualDesktop *vd = p->q->getVirtualDesktop(stringId); + //TODO: emit a lot of desktopMoved? + Q_ASSERT(vd); + auto i = p->findDesktop(stringId); + p->desktops.erase(i); + vd->release(); + vd->destroy(); + vd->deleteLater(); + emit p->q->desktopRemoved(stringId); +} + +void PlasmaVirtualDesktopManagement::Private::doneCallback(void *data, org_kde_plasma_virtual_desktop_management *org_kde_plasma_virtual_desktop_management) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->plasmavirtualdesktopmanagement == org_kde_plasma_virtual_desktop_management); + emit p->q->done(); +} + +PlasmaVirtualDesktopManagement::PlasmaVirtualDesktopManagement(QObject *parent) + : QObject(parent) + , d(new Private(this)) +{ +} + +PlasmaVirtualDesktopManagement::Private::Private(PlasmaVirtualDesktopManagement *q) + : q(q) +{} + +void PlasmaVirtualDesktopManagement::Private::setup(org_kde_plasma_virtual_desktop_management *arg) +{ + Q_ASSERT(arg); + Q_ASSERT(!plasmavirtualdesktopmanagement); + plasmavirtualdesktopmanagement.setup(arg); + org_kde_plasma_virtual_desktop_management_add_listener(plasmavirtualdesktopmanagement, &s_listener, this); +} + +PlasmaVirtualDesktopManagement::~PlasmaVirtualDesktopManagement() +{ + release(); +} + +void PlasmaVirtualDesktopManagement::setup(org_kde_plasma_virtual_desktop_management *plasmavirtualdesktopmanagement) +{ + d->setup(plasmavirtualdesktopmanagement); +} + +void PlasmaVirtualDesktopManagement::release() +{ + d->plasmavirtualdesktopmanagement.release(); +} + +void PlasmaVirtualDesktopManagement::destroy() +{ + d->plasmavirtualdesktopmanagement.destroy(); +} + +PlasmaVirtualDesktopManagement::operator org_kde_plasma_virtual_desktop_management*() { + return d->plasmavirtualdesktopmanagement; +} + +PlasmaVirtualDesktopManagement::operator org_kde_plasma_virtual_desktop_management*() const { + return d->plasmavirtualdesktopmanagement; +} + +bool PlasmaVirtualDesktopManagement::isValid() const +{ + return d->plasmavirtualdesktopmanagement.isValid(); +} + +void PlasmaVirtualDesktopManagement::setEventQueue(EventQueue *queue) +{ + d->queue = queue; +} + +EventQueue *PlasmaVirtualDesktopManagement::eventQueue() +{ + return d->queue; +} + +PlasmaVirtualDesktop *PlasmaVirtualDesktopManagement::getVirtualDesktop(const QString &id) +{ + Q_ASSERT(isValid()); + + auto i = d->constFindDesktop(id); + if (i != d->desktops.constEnd()) { + return *i; + } + + auto w = org_kde_plasma_virtual_desktop_management_get_virtual_desktop(d->plasmavirtualdesktopmanagement, id.toUtf8()); + + if (!w) { + return nullptr; + } + + if (d->queue) { + d->queue->addProxy(w); + } + + auto desktop = new PlasmaVirtualDesktop(this); + desktop->setup(w); + + return desktop; +} + +QList PlasmaVirtualDesktopManagement::desktops() const +{ + return d->desktops; +} + +class Q_DECL_HIDDEN PlasmaVirtualDesktop::Private +{ +public: + Private(PlasmaVirtualDesktop *q); + + void setup(org_kde_plasma_virtual_desktop *arg); + + WaylandPointer plasmavirtualdesktop; + + QString id; + QString name; + bool active = false; + +private: + PlasmaVirtualDesktop *q; + +private: + static void idCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop, const char * id); + static void nameCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop, const char * name); + + static void activatedCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop); + static void deactivatedCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop); + static void doneCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop); + static void removedCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop); + + static const org_kde_plasma_virtual_desktop_listener s_listener; +}; + +const org_kde_plasma_virtual_desktop_listener PlasmaVirtualDesktop::Private::s_listener = { + idCallback, + nameCallback, + activatedCallback, + deactivatedCallback, + doneCallback, + removedCallback +}; + +void PlasmaVirtualDesktop::Private::idCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop, const char * id) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->plasmavirtualdesktop == org_kde_plasma_virtual_desktop); + p->id = QString::fromUtf8(id); +} + +void PlasmaVirtualDesktop::Private::nameCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop, const char * name) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->plasmavirtualdesktop == org_kde_plasma_virtual_desktop); + p->name = QString::fromUtf8(name); +} + +void PlasmaVirtualDesktop::Private::activatedCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->plasmavirtualdesktop == org_kde_plasma_virtual_desktop); + p->active = true; + emit p->q->activated(); +} + +void PlasmaVirtualDesktop::Private::deactivatedCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->plasmavirtualdesktop == org_kde_plasma_virtual_desktop); + p->active = false; + emit p->q->deactivated(); +} + +void PlasmaVirtualDesktop::Private::doneCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->plasmavirtualdesktop == org_kde_plasma_virtual_desktop); + emit p->q->done(); +} + +void PlasmaVirtualDesktop::Private::removedCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->plasmavirtualdesktop == org_kde_plasma_virtual_desktop); + emit p->q->removed(); +} + +PlasmaVirtualDesktop::Private::Private(PlasmaVirtualDesktop *q) + : q(q) +{ +} + +PlasmaVirtualDesktop::PlasmaVirtualDesktop(QObject *parent) + : QObject(parent) + , d(new Private(this)) +{ +} + +void PlasmaVirtualDesktop::Private::setup(org_kde_plasma_virtual_desktop *arg) +{ + Q_ASSERT(arg); + Q_ASSERT(!plasmavirtualdesktop); + plasmavirtualdesktop.setup(arg); + org_kde_plasma_virtual_desktop_add_listener(plasmavirtualdesktop, &s_listener, this); +} + +PlasmaVirtualDesktop::~PlasmaVirtualDesktop() +{ + release(); +} + +void PlasmaVirtualDesktop::setup(org_kde_plasma_virtual_desktop *plasmavirtualdesktop) +{ + d->setup(plasmavirtualdesktop); +} + +void PlasmaVirtualDesktop::release() +{ + d->plasmavirtualdesktop.release(); +} + +void PlasmaVirtualDesktop::destroy() +{ + d->plasmavirtualdesktop.destroy(); +} + +PlasmaVirtualDesktop::operator org_kde_plasma_virtual_desktop*() { + return d->plasmavirtualdesktop; +} + +PlasmaVirtualDesktop::operator org_kde_plasma_virtual_desktop*() const { + return d->plasmavirtualdesktop; +} + +bool PlasmaVirtualDesktop::isValid() const +{ + return d->plasmavirtualdesktop.isValid(); +} + +void PlasmaVirtualDesktop::requestActivate() +{ + Q_ASSERT(isValid()); + org_kde_plasma_virtual_desktop_request_activate(d->plasmavirtualdesktop); +} + +QString PlasmaVirtualDesktop::id() const +{ + return d->id; +} + +QString PlasmaVirtualDesktop::name() const +{ + return d->name; +} + +bool PlasmaVirtualDesktop::active() const +{ + return d->active; +} + +} +} + diff --git a/src/client/plasmawindowmanagement.h b/src/client/plasmawindowmanagement.h --- a/src/client/plasmawindowmanagement.h +++ b/src/client/plasmawindowmanagement.h @@ -37,6 +37,7 @@ class PlasmaWindow; class PlasmaWindowModel; class Surface; +class PlasmaVirtualDesktop; /** * @short Wrapper for the org_kde_plasma_window_management interface. @@ -272,6 +273,8 @@ **/ QString appId() const; /** + * DEPRECATED: use plasmaVirtualDesktops instead + * @see plasmaVirtualDesktops * @returns the id of the virtual desktop this PlasmaWindow is on * @see virtualDesktopChanged **/ @@ -307,6 +310,8 @@ **/ bool isMaximized() const; /** + * DEPRECATED + * TODO: make this work with plasmaVirtualDesktops instead? * @returns Whether the window is shown on all desktops. * @see virtualDesktop * @see onAllDesktopsChanged @@ -373,6 +378,7 @@ */ bool isResizable() const; /** + * DEPRECATED * @returns Whether the virtual desktop can be changed. * @see virtualDesktopChangeableChanged * @since 5.22 @@ -404,6 +410,7 @@ */ void requestResize(); /** + * DEPRECATED: use requestEnterVirtualDesktop instead * Requests to send the window to virtual @p desktop. **/ void requestVirtualDesktop(quint32 desktop); @@ -475,6 +482,33 @@ **/ QRect geometry() const; + /** + * Ask the server to make the window enter a virtual desktop. + * The server may or may not consent. + * A window can enter more than one virtual desktop. + * + * @since 5.46 + */ + void requestEnterVirtualDesktop(const QString &id); + + /** + * Ask the server to make the window the window exit a virtual desktop. + * The server may or may not consent. + * If it exits all desktops it will be considered on all of them. + * + * @since 5.46 + */ + void requestLeaveVirtualDesktop(const QString &id); + + /** + * Return all the virtual desktop ids this window is associated to. + * When a desktop gets deleted, it will be automatically removed from this list. + * If this list is empty, assume it's on all desktops. + * + * @since 5.46 + */ + QStringList plasmaVirtualDesktops() const; + Q_SIGNALS: /** * The window title changed. @@ -487,6 +521,7 @@ **/ void appIdChanged(); /** + * DEPRECATED * The virtual desktop changed. * @see virtualDesktop **/ @@ -586,6 +621,7 @@ */ void resizableChanged(); /** + * DEPRECATED * The virtual desktop changeable state changed. * @see virtualDesktopChangeable * @since 5.22 @@ -610,6 +646,21 @@ **/ void geometryChanged(); + /** + * This signal is emitted when the window has entered a new virtual desktop. + * The window can be on more than one desktop, or none: then is considered on all of them. + * @since 5.46 + */ + void plasmaVirtualDesktopEntered(const QString &id); + + /** + * This signal is emitted when the window left a virtual desktop. + * If the window leaves all desktops, it can be considered on all. + * + * @since 5.46 + */ + void plasmaVirtualDesktopLeft(const QString &id); + private: friend class PlasmaWindowManagement; explicit PlasmaWindow(PlasmaWindowManagement *parent, org_kde_plasma_window *dataOffer, quint32 internalId); diff --git a/src/client/plasmawindowmanagement.cpp b/src/client/plasmawindowmanagement.cpp --- a/src/client/plasmawindowmanagement.cpp +++ b/src/client/plasmawindowmanagement.cpp @@ -19,6 +19,7 @@ *********************************************************************/ #include "plasmawindowmanagement.h" #include "plasmawindowmodel.h" +#include "plasmavirtualdesktop.h" #include "event_queue.h" #include "output.h" #include "surface.h" @@ -92,6 +93,7 @@ bool unmapped = false; QPointer parentWindow; QMetaObject::Connection parentWindowUnmappedConnection; + QStringList plasmaVirtualDesktops; QRect geometry; quint32 pid = 0; @@ -107,6 +109,8 @@ 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); @@ -345,7 +349,9 @@ parentWindowCallback, windowGeometryCallback, iconChangedCallback, - pidChangedCallback + pidChangedCallback, + virtualDesktopEnteredCallback, + virtualDesktopLeftCallback }; void PlasmaWindow::Private::parentWindowCallback(void *data, org_kde_plasma_window *window, org_kde_plasma_window *parent) @@ -456,6 +462,24 @@ 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); +} + +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); +} + void PlasmaWindow::Private::stateChangedCallback(void *data, org_kde_plasma_window *window, uint32_t state) { auto p = cast(data); @@ -993,5 +1017,20 @@ return d->geometry; } +void PlasmaWindow::requestEnterVirtualDesktop(const QString &id) +{ + org_kde_plasma_window_request_enter_virtual_desktop(d->window, id.toUtf8()); +} + +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/protocols/plasma-virtual-desktop.xml b/src/client/protocols/plasma-virtual-desktop.xml new file mode 100644 --- /dev/null +++ b/src/client/protocols/plasma-virtual-desktop.xml @@ -0,0 +1,98 @@ + + + . + ]]> + + + + + Given the id of a particular virtual desktop, get the corresponding org_kde_plasma_virtual_desktop which represents only the desktop with that id; + + + + + + + + + + + + + + + + + + + + + + + + + This event is sent after all other properties has been + sent after binding to the desktop manager object and after any + other property changes done after that. This allows + changes to the org_kde_plasma_virtual_desktop_management properties to be seen as + atomic, even if they happen via multiple events. + + + + + + + + + + + + + The format of the id is decided by the compositor implementation. + + + + + + + + + + + + + + + + + This event is sent after all other properties has been + sent after binding to the desktop object and after any + other property changes done after that. This allows + changes to the org_kde_plasma_virtual_desktop properties to be seen as + atomic, even if they happen via multiple events. + + + + + + This virtual desktop has just been removed by the server: + All windows will lose the association to this desktop. + + + + + diff --git a/src/client/protocols/plasma-window-management.xml b/src/client/protocols/plasma-window-management.xml --- a/src/client/protocols/plasma-window-management.xml +++ b/src/client/protocols/plasma-window-management.xml @@ -83,7 +83,7 @@ - + Manages and control an application window. @@ -104,6 +104,7 @@ + Deprecated: use enter_virtual_desktop Maps the window to a different virtual desktop. To show the window on all virtual desktops, call the @@ -266,5 +267,37 @@ + + + + + + Make the window enter a virtual desktop. A window can enter more + than one virtual desktop. + + + + + + + Make the window exit a virtual desktop. If it exits all desktops it will be considered on all of them. + + + + + + + This event will be sent when the window has entered a new virtual desktop. The window can be on more than one desktop, or none: then is considered on all of them. + + + + + + + This event will be sent when the window left a virtual desktop. If the window leaves all desktops, it can be considered on all. + + + + diff --git a/src/client/registry.h b/src/client/registry.h --- a/src/client/registry.h +++ b/src/client/registry.h @@ -49,6 +49,7 @@ struct org_kde_kwin_contrast_manager; struct org_kde_kwin_slide_manager; struct org_kde_plasma_shell; +struct org_kde_plasma_virtual_desktop_management; struct org_kde_plasma_window_management; struct org_kde_kwin_server_decoration_manager; struct org_kde_kwin_server_decoration_palette_manager; @@ -81,6 +82,7 @@ class RemoteAccessManager; class Output; class PlasmaShell; +class PlasmaVirtualDesktopManagement; class PlasmaWindowManagement; class PointerConstraints; class PointerGestures; @@ -172,7 +174,8 @@ IdleInhibitManagerUnstableV1, ///< Refers to zwp_idle_inhibit_manager_v1 (unstable version 1), @since 5.41 AppMenu, ///Refers to org_kde_kwin_appmenu @since 5.42 ServerSideDecorationPalette, ///Refers to org_kde_kwin_server_decoration_palette_manager @since 5.42 - RemoteAccessManager ///< Refers to org_kde_kwin_remote_access_manager interface, @since 5.45 + RemoteAccessManager, ///< Refers to org_kde_kwin_remote_access_manager interface, @since 5.45 + PlasmaVirtualDesktopManagement, ///< Refers to org_kde_plasma_virtual_desktop_management interface @since 5.46 }; explicit Registry(QObject *parent = nullptr); virtual ~Registry(); @@ -388,6 +391,16 @@ **/ org_kde_plasma_shell *bindPlasmaShell(uint32_t name, uint32_t version) const; /** + * Binds the org_kde_plasma_virtual_desktop_management with @p name and @p version. + * If the @p name does not exist or is not for the Plasma Virtual desktop interface, + * @c null will be returned. + * + * Prefer using createPlasmaShell instead. + * @see createPlasmaShell + * @since 5.46 + **/ + org_kde_plasma_virtual_desktop_management *bindPlasmaVirtualDesktopManagement(uint32_t name, uint32_t version) const; + /** * Binds the org_kde_plasma_window_management with @p name and @p version. * If the @p name does not exist or is not for the Plasma window management interface, * @c null will be returned. @@ -787,6 +800,22 @@ **/ PlasmaShell *createPlasmaShell(quint32 name, quint32 version, QObject *parent = nullptr); /** + * Creates a PlasmaVirtualDesktopManagement and sets it up to manage the interface identified by + * @p name and @p version. + * + * Note: in case @p name is invalid or isn't for the org_kde_plasma_virtual_desktop_management interface, + * the returned VirtualDesktop will not be valid. Therefore it's recommended to call + * isValid on the created instance. + * + * @param name The name of the org_kde_plasma_virtual_desktop_management interface to bind + * @param version The version or the org_kde_plasma_virtual_desktop_management interface to use + * @param parent The parent for PlasmaShell + * + * @returns The created PlasmaShell. + * @since 5.46 + **/ + PlasmaVirtualDesktopManagement *createPlasmaVirtualDesktopManagement(quint32 name, quint32 version, QObject *parent = nullptr); + /** * Creates a PlasmaWindowManagement and sets it up to manage the interface identified by * @p name and @p version. * @@ -1202,6 +1231,13 @@ **/ void plasmaShellAnnounced(quint32 name, quint32 version); /** + * Emitted whenever a org_kde_plasma_virtual_desktop_management interface gets announced. + * @param name The name for the announced interface + * @param version The maximum supported version of the announced interface + * @since 5.46 + **/ + void plasmaVirtualDesktopManagementAnnounced(quint32 name, quint32 version); + /** * Emitted whenever a org_kde_plasma_window_management interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface @@ -1427,6 +1463,12 @@ **/ void plasmaShellRemoved(quint32 name); /** + * Emitted whenever a org_kde_plasma_virtual_desktop_management interface gets removed. + * @param name The name for the removed interface + * @since 5.46 + **/ + void plasmaVirtualDesktopManagementRemoved(quint32 name); + /** * Emitted whenever a org_kde_plasma_window_management interface gets removed. * @param name The name for the removed interface * @since 5.4 diff --git a/src/client/registry.cpp b/src/client/registry.cpp --- a/src/client/registry.cpp +++ b/src/client/registry.cpp @@ -34,6 +34,7 @@ #include "outputdevice.h" #include "output.h" #include "plasmashell.h" +#include "plasmavirtualdesktop.h" #include "plasmawindowmanagement.h" #include "pointerconstraints.h" #include "pointergestures.h" @@ -60,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -167,8 +169,15 @@ &Registry::plasmaShellAnnounced, &Registry::plasmaShellRemoved }}, + {Registry::Interface::PlasmaVirtualDesktopManagement, { + 1, + QByteArrayLiteral("org_kde_plasma_virtual_desktop_management"), + &org_kde_plasma_virtual_desktop_management_interface, + &Registry::plasmaVirtualDesktopManagementAnnounced, + &Registry::plasmaVirtualDesktopManagementRemoved + }}, {Registry::Interface::PlasmaWindowManagement, { - 7, + 8, QByteArrayLiteral("org_kde_plasma_window_management"), &org_kde_plasma_window_management_interface, &Registry::plasmaWindowManagementAnnounced, @@ -629,6 +638,7 @@ BIND(FullscreenShell, _wl_fullscreen_shell) BIND(DataDeviceManager, wl_data_device_manager) BIND(PlasmaShell, org_kde_plasma_shell) +BIND(PlasmaVirtualDesktopManagement, org_kde_plasma_virtual_desktop_management) BIND(PlasmaWindowManagement, org_kde_plasma_window_management) BIND(Idle, org_kde_kwin_idle) BIND(RemoteAccessManager, org_kde_kwin_remote_access_manager) @@ -690,6 +700,7 @@ CREATE(Output) CREATE(DataDeviceManager) CREATE(PlasmaShell) +CREATE(PlasmaVirtualDesktopManagement) CREATE(PlasmaWindowManagement) CREATE(Idle) CREATE(RemoteAccessManager) diff --git a/src/server/CMakeLists.txt b/src/server/CMakeLists.txt --- a/src/server/CMakeLists.txt +++ b/src/server/CMakeLists.txt @@ -25,6 +25,7 @@ output_interface.cpp pointer_interface.cpp plasmashell_interface.cpp + plasmavirtualdesktop_interface.cpp plasmawindowmanagement_interface.cpp pointerconstraints_interface.cpp pointerconstraints_interface_v1.cpp @@ -72,6 +73,11 @@ ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS + PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/plasma-virtual-desktop.xml + BASENAME plasma-virtual-desktop +) + +ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/plasma-window-management.xml BASENAME plasma-window-management ) @@ -187,6 +193,9 @@ ${CMAKE_CURRENT_BINARY_DIR}/wayland-org_kde_kwin_outputdevice-server-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-shell-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-shell-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-shell-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-virtual-desktop-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-virtual-desktop-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-window-management-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-window-management-server-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-qt-surface-extension-client-protocol.h @@ -289,6 +298,7 @@ pointerconstraints_interface.h pointergestures_interface.h plasmashell_interface.h + plasmavirtualdesktop_interface.h plasmawindowmanagement_interface.h qtsurfaceextension_interface.h region_interface.h diff --git a/src/server/display.h b/src/server/display.h --- a/src/server/display.h +++ b/src/server/display.h @@ -86,6 +86,7 @@ class XdgForeignInterface; class AppMenuManagerInterface; class ServerSideDecorationPaletteManagerInterface; +class PlasmaVirtualDesktopManagementInterface; /** * @brief Class holding the Wayland server display loop. @@ -260,6 +261,14 @@ /** + * Creates the PlasmaVirtualDesktopManagementInterface in interface @p version. + * + * @returns The created manager object + * @since 5.46 + **/ + PlasmaVirtualDesktopManagementInterface *createPlasmaVirtualDesktopManagement(QObject *parent = nullptr); + + /** * Gets the ClientConnection for the given @p client. * If there is no ClientConnection yet for the given @p client, it will be created. * @param client The native client for which the ClientConnection is retrieved diff --git a/src/server/display.cpp b/src/server/display.cpp --- a/src/server/display.cpp +++ b/src/server/display.cpp @@ -50,6 +50,7 @@ #include "xdgshell_v6_interface_p.h" #include "appmenu_interface.h" #include "server_decoration_palette_interface.h" +#include "plasmavirtualdesktop_interface.h" #include #include @@ -457,6 +458,13 @@ return b; } +PlasmaVirtualDesktopManagementInterface *Display::createPlasmaVirtualDesktopManagement(QObject *parent) +{ + auto b = new PlasmaVirtualDesktopManagementInterface(this, parent); + connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; }); + return b; +} + void Display::createShm() { Q_ASSERT(d->display); diff --git a/src/server/plasmavirtualdesktop_interface.h b/src/server/plasmavirtualdesktop_interface.h new file mode 100644 --- /dev/null +++ b/src/server/plasmavirtualdesktop_interface.h @@ -0,0 +1,147 @@ +/**************************************************************************** +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 . +****************************************************************************/ +#ifndef KWAYLAND_SERVER_PLASMAVIRTUALDESKTOP_H +#define KWAYLAND_SERVER_PLASMAVIRTUALDESKTOP_H + +#include "global.h" +#include "resource.h" + +#include + +namespace KWayland +{ +namespace Server +{ + +class Display; +class PlasmaVirtualDesktopInterface; + +/** + * @short Wrapper for the org_kde_plasma_virtual_desktop_management interface. + * + * This class provides a convenient wrapper for the org_kde_plasma_virtual_desktop_management interface. + * @since 5.46 + */ +class KWAYLANDSERVER_EXPORT PlasmaVirtualDesktopManagementInterface : public Global +{ + Q_OBJECT +public: + virtual ~PlasmaVirtualDesktopManagementInterface(); + + /** + * Sets a new layout for this desktop grid. + */ + void setLayout(quint32 rows, quint32 columns); + + /** + * @returns A desktop identified uniquely by this id. + * If not found, nullptr will be returned. + * @see createDesktop + */ + PlasmaVirtualDesktopInterface *desktop(const QString &id); + + /** + * @returns A desktop identified uniquely by this id, if not found + * a new desktop will be created for this id. + */ + PlasmaVirtualDesktopInterface *createDesktop(const QString &id); + + /** + * Removed and destroys the desktop identified by id, if present + */ + void removeDesktop(const QString &id); + + /** + * @returns All tghe desktops present. + */ + QList desktops() const; + + /** + * Inform the clients that all the properties have been sent, and + * their client-side representation is complete. + */ + void sendDone(); + + /** + * Sets the desktop identified by id to be the active one. + * active desktops are mutually exclusive + */ + void setActiveDesktop(const QString &id); + +private: + explicit PlasmaVirtualDesktopManagementInterface(Display *display, QObject *parent = nullptr); + friend class Display; + class Private; + Private *d_func() const; +}; + +class KWAYLANDSERVER_EXPORT PlasmaVirtualDesktopInterface : public QObject +{ + Q_OBJECT +public: + virtual ~PlasmaVirtualDesktopInterface(); + + /** + * @returns the unique id for this desktop. + * ids are set at creation time by PlasmaVirtualDesktopManagementInterface::createDesktop + * and can't be changed at runtime. + */ + QString id() const; + + /** + * Sets a new name for this desktop + */ + void setName(const QString &name); + + /** + * @returns the name for this desktop + */ + QString name() const; + + /** + * @returns true if this desktop is active. Only one at a time will be. + */ + bool active() const; + + /** + * Inform the clients that all the properties have been sent, and + * their client-side representation is complete. + */ + void sendDone(); + +Q_SIGNALS: + /** + * Emitted when the client asked to activate this desktop: + * it's the decision of the server whether to perform the activation or not. + */ + void activateRequested(); + +private: + explicit PlasmaVirtualDesktopInterface(PlasmaVirtualDesktopManagementInterface *parent); + friend class PlasmaVirtualDesktopManagementInterface; + + class Private; + const QScopedPointer d; +}; + +} +} + +#endif diff --git a/src/server/plasmavirtualdesktop_interface.cpp b/src/server/plasmavirtualdesktop_interface.cpp new file mode 100644 --- /dev/null +++ b/src/server/plasmavirtualdesktop_interface.cpp @@ -0,0 +1,413 @@ +/**************************************************************************** +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 . +****************************************************************************/ +#include "plasmavirtualdesktop_interface.h" +#include "display.h" +#include "global_p.h" +#include "resource_p.h" + +#include +#include + +#include +#include + +namespace KWayland +{ +namespace Server +{ + +class Q_DECL_HIDDEN PlasmaVirtualDesktopInterface::Private +{ +public: + Private(PlasmaVirtualDesktopInterface *q, PlasmaVirtualDesktopManagementInterface *c); + ~Private(); + void createResource(wl_resource *parent, quint32 serial); + + PlasmaVirtualDesktopInterface *q; + PlasmaVirtualDesktopManagementInterface *vdm; + + QVector resources; + QString id; + QString name; + bool active = false; + +private: + static void unbind(wl_resource *resource); + static void requestActivateCallback(wl_client *client, wl_resource *resource); + + static Private *cast(wl_resource *resource) { + return reinterpret_cast(wl_resource_get_user_data(resource)); + } + + wl_listener listener; + static const struct org_kde_plasma_virtual_desktop_interface s_interface; +}; + + +class Q_DECL_HIDDEN PlasmaVirtualDesktopManagementInterface::Private : public Global::Private +{ +public: + Private(PlasmaVirtualDesktopManagementInterface *q, Display *d); + + QVector resources; + QList desktops; + quint32 rows = 0; + quint32 columns = 0; + + inline QList::const_iterator constFindDesktop(const QString &id); + inline QList::iterator findDesktop(const QString &id); +private: + void bind(wl_client *client, uint32_t version, uint32_t id) override; + + static void unbind(wl_resource *resource); + static Private *cast(wl_resource *r) { + return reinterpret_cast(wl_resource_get_user_data(r)); + } + + static void getVirtualDesktopCallback(wl_client *client, wl_resource *resource, uint32_t serial, const char *id); + static void releaseCallback(wl_client *client, wl_resource *resource); + + PlasmaVirtualDesktopManagementInterface *q; + + static const struct org_kde_plasma_virtual_desktop_management_interface s_interface; + static const quint32 s_version; +}; + +const quint32 PlasmaVirtualDesktopManagementInterface::Private::s_version = 1; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct org_kde_plasma_virtual_desktop_management_interface PlasmaVirtualDesktopManagementInterface::Private::s_interface = { + getVirtualDesktopCallback, + releaseCallback +}; +#endif + +inline QList::const_iterator PlasmaVirtualDesktopManagementInterface::Private::constFindDesktop(const QString &id) +{ + return std::find_if( desktops.constBegin(), + desktops.constEnd(), + [id]( const PlasmaVirtualDesktopInterface *desk ){ return desk->id() == id; } ); +} + +inline QList::iterator PlasmaVirtualDesktopManagementInterface::Private::findDesktop(const QString &id) +{ + return std::find_if( desktops.begin(), + desktops.end(), + [id]( const PlasmaVirtualDesktopInterface *desk ){ return desk->id() == id; } ); +} + +void PlasmaVirtualDesktopManagementInterface::Private::getVirtualDesktopCallback(wl_client *client, wl_resource *resource, uint32_t serial, const char *id) +{ + Q_UNUSED(client) + auto s = cast(resource); + + auto i = s->constFindDesktop(QString::fromUtf8(id)); + if (i == s->desktops.constEnd()) { + return; + } + + (*i)->d->createResource(resource, serial); +} + +void PlasmaVirtualDesktopManagementInterface::Private::releaseCallback(wl_client *client, wl_resource *resource) +{ + Q_UNUSED(client) + wl_resource_destroy(resource); +} + +PlasmaVirtualDesktopManagementInterface::Private::Private(PlasmaVirtualDesktopManagementInterface *q, Display *d) + : Global::Private(d, &org_kde_plasma_virtual_desktop_management_interface, s_version) + , q(q) +{ +} + +void PlasmaVirtualDesktopManagementInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) +{ + auto c = display->getConnection(client); + wl_resource *resource = c->createResource(&org_kde_plasma_virtual_desktop_management_interface, qMin(version, s_version), id); + + if (!resource) { + wl_client_post_no_memory(client); + return; + } + resources << resource; + + wl_resource_set_implementation(resource, &s_interface, this, unbind); + + quint32 i = 0; + for (auto it = desktops.constBegin(); it != desktops.constEnd(); ++it) { + org_kde_plasma_virtual_desktop_management_send_desktop_added(resource, (*it)->id().toUtf8().constData(), i++); + } +} + +void PlasmaVirtualDesktopManagementInterface::Private::unbind(wl_resource *resource) +{ + auto dm = reinterpret_cast(wl_resource_get_user_data(resource)); + dm->resources.removeAll(resource); +} + +PlasmaVirtualDesktopManagementInterface::PlasmaVirtualDesktopManagementInterface(Display *display, QObject *parent) + : Global(new Private(this, display), parent) +{ +} + +PlasmaVirtualDesktopManagementInterface::~PlasmaVirtualDesktopManagementInterface() +{} + +PlasmaVirtualDesktopManagementInterface::Private *PlasmaVirtualDesktopManagementInterface::d_func() const +{ + return reinterpret_cast(d.data()); +} + +PlasmaVirtualDesktopInterface *PlasmaVirtualDesktopManagementInterface::desktop(const QString &id) +{ + Q_D(); + auto i = d->constFindDesktop(id); + if (i != d->desktops.constEnd()) { + return *i; + } + return nullptr; +} + +PlasmaVirtualDesktopInterface *PlasmaVirtualDesktopManagementInterface::createDesktop(const QString &id) +{ + Q_D(); + auto i = d->constFindDesktop(id); + if (i != d->desktops.constEnd()) { + return *i; + } + + PlasmaVirtualDesktopInterface *desktop = new PlasmaVirtualDesktopInterface(this); + desktop->d->id = id; + for (auto it = desktop->d->resources.constBegin(); it != desktop->d->resources.constEnd(); ++it) { + org_kde_plasma_virtual_desktop_send_id(*it, id.toUtf8().constData()); + } + + //activate the first desktop TODO: to be done here? + if (d->desktops.isEmpty()) { + desktop->d->active = true; + } + + d->desktops << desktop; + //NOTE: this in case the desktop has been deleted but not trough removedesktop + connect(desktop, &QObject::destroyed, this, + [this, id] { + Q_D(); + auto i = d->findDesktop(id); + if (i != d->desktops.end()) { + for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { + org_kde_plasma_virtual_desktop_management_send_desktop_removed(*it, id.toUtf8().constData()); + } + //Activate another desktop + if (d->desktops.length() > 0 && (*i)->active()) { + if (i == d->desktops.begin()) { + setActiveDesktop((*(i + 1))->id()); + } else { + setActiveDesktop((*(i - 1))->id()); + } + } + d->desktops.erase(i); + } + } + ); + + for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { + org_kde_plasma_virtual_desktop_management_send_desktop_added(*it, id.toUtf8().constData(), d->desktops.length()-1); + } + + return desktop; +} + +void PlasmaVirtualDesktopManagementInterface::removeDesktop(const QString &id) +{ + Q_D(); + auto deskIt = d->findDesktop(id); + if (deskIt == d->desktops.end()) { + return; + } + + for (auto it = (*deskIt)->d->resources.constBegin(); it != (*deskIt)->d->resources.constEnd(); ++it) { + org_kde_plasma_virtual_desktop_send_removed(*it); + } + + for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { + org_kde_plasma_virtual_desktop_management_send_desktop_removed(*it, id.toUtf8().constData()); + } + + //Activate another desktop + if (d->desktops.length() > 0 && (*deskIt)->active()) { + if (deskIt == d->desktops.begin()) { + setActiveDesktop((*(deskIt + 1))->id()); + } else { + setActiveDesktop((*(deskIt - 1))->id()); + } + } + + d->desktops.erase(deskIt); + (*deskIt)->deleteLater(); +} + +QList PlasmaVirtualDesktopManagementInterface::desktops() const +{ + Q_D(); + return d->desktops; +} + +void PlasmaVirtualDesktopManagementInterface::sendDone() +{ + Q_D(); + for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { + org_kde_plasma_virtual_desktop_management_send_done(*it); + } +} + +void PlasmaVirtualDesktopManagementInterface::setActiveDesktop(const QString &id) +{ + Q_D(); + for (auto it = d->desktops.constBegin(); it != d->desktops.constEnd(); ++it) { + auto desktop = *it; + if (desktop->id() == id) { + desktop->d->active = true; + for (auto it = desktop->d->resources.constBegin(); it != desktop->d->resources.constEnd(); ++it) { + org_kde_plasma_virtual_desktop_send_activated(*it); + } + } else { + if (desktop->d->active) { + desktop->d->active = false; + for (auto it = desktop->d->resources.constBegin(); it != desktop->d->resources.constEnd(); ++it) { + org_kde_plasma_virtual_desktop_send_deactivated(*it); + } + } + } + } +} + + + +//// PlasmaVirtualDesktopInterface + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct org_kde_plasma_virtual_desktop_interface PlasmaVirtualDesktopInterface::Private::s_interface = { + requestActivateCallback +}; +#endif + +void PlasmaVirtualDesktopInterface::Private::requestActivateCallback(wl_client *client, wl_resource *resource) +{ + Q_UNUSED(client) + auto s = cast(resource); + emit s->q->activateRequested(); +} + +PlasmaVirtualDesktopInterface::Private::Private(PlasmaVirtualDesktopInterface *q, PlasmaVirtualDesktopManagementInterface *c) + : q(q), + vdm(c) +{ +} + +PlasmaVirtualDesktopInterface::Private::~Private() +{ + // need to copy, as destroy goes through the destroy listener and modifies the list as we iterate + const auto c = resources; + for (const auto &r : c) { + auto client = wl_resource_get_client(r); + org_kde_plasma_virtual_desktop_send_removed(r); + wl_resource_destroy(r); + wl_client_flush(client); + } +} + +void PlasmaVirtualDesktopInterface::Private::unbind(wl_resource *resource) +{ + Private *p = reinterpret_cast(wl_resource_get_user_data(resource)); + p->resources.removeAll(resource); + if (p->resources.isEmpty()) { + p->q->deleteLater(); + } +} + +void PlasmaVirtualDesktopInterface::Private::createResource(wl_resource *parent, quint32 serial) +{ + ClientConnection *c = vdm->display()->getConnection(wl_resource_get_client(parent)); + wl_resource *resource = c->createResource(&org_kde_plasma_virtual_desktop_interface, wl_resource_get_version(parent), serial); + if (!resource) { + return; + } + wl_resource_set_implementation(resource, &s_interface, this, unbind); + resources << resource; + + org_kde_plasma_virtual_desktop_send_id(resource, id.toUtf8().constData()); + if (!name.isEmpty()) { + org_kde_plasma_virtual_desktop_send_name(resource, name.toUtf8().constData()); + } + + if (active) { + org_kde_plasma_virtual_desktop_send_activated(resource); + } + + c->flush(); +} + +PlasmaVirtualDesktopInterface::PlasmaVirtualDesktopInterface(PlasmaVirtualDesktopManagementInterface *parent) + : QObject(parent), + d(new Private(this, parent)) +{ +} + +PlasmaVirtualDesktopInterface::~PlasmaVirtualDesktopInterface() +{} + +QString PlasmaVirtualDesktopInterface::id() const +{ + return d->id; +} + +void PlasmaVirtualDesktopInterface::setName(const QString &name) +{ + if (d->name == name) { + return; + } + + d->name = name; + for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { + org_kde_plasma_virtual_desktop_send_name(*it, name.toUtf8().constData()); + } +} + +QString PlasmaVirtualDesktopInterface::name() const +{ + return d->name; +} + +bool PlasmaVirtualDesktopInterface::active() const +{ + return d->active; +} + +void PlasmaVirtualDesktopInterface::sendDone() +{ + for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { + org_kde_plasma_virtual_desktop_send_done(*it); + } +} + +} +} + diff --git a/src/server/plasmawindowmanagement_interface.h b/src/server/plasmawindowmanagement_interface.h --- a/src/server/plasmawindowmanagement_interface.h +++ b/src/server/plasmawindowmanagement_interface.h @@ -37,6 +37,7 @@ class Display; class PlasmaWindowInterface; class SurfaceInterface; +class PlasmaVirtualDesktopManagementInterface; /** * @todo Add documentation @@ -72,6 +73,20 @@ **/ void unmapWindow(PlasmaWindowInterface *window); + /** + * Associate a PlasmaVirtualDesktopManagementInterface to this window management. + * It's necessary to associate one in orderto use the plasma virtual desktop features + * of PlasmaWindowInterface, as a window must know what are the deasktops available + * @since 5.46 + */ + void setPlasmaVirtualDesktopManagementInterface(PlasmaVirtualDesktopManagementInterface *manager); + + /** + * @returns the PlasmaVirtualDesktopManagementInterface associated to this PlasmaWindowManagementInterface + * @since 5.46 + */ + PlasmaVirtualDesktopManagementInterface *plasmaVirtualDesktopManagementInterface() const; + Q_SIGNALS: void requestChangeShowingDesktop(ShowingDesktopState requestedState); @@ -94,6 +109,10 @@ void setTitle(const QString &title); void setAppId(const QString &appId); void setPid(quint32 pid); + /** + * DEPRECATED + * @see addPlasmaVirtualDesktop + */ void setVirtualDesktop(quint32 desktop); void setActive(bool set); void setMinimized(bool set); @@ -132,6 +151,8 @@ */ void setResizable(bool set); /** + * DEPRECATED + * @see addPlasmaVirtualDesktop * @since 5.22 */ void setVirtualDesktopChangeable(bool set); @@ -181,6 +202,30 @@ **/ void setIcon(const QIcon &icon); + /** + * Adds a new desktop to this window: a window can be on + * an arbitrary subset of virtual desktops. + * If it's on none it will be considered on all desktops. + * + * @since 5.46 + */ + void addPlasmaVirtualDesktop(const QString &id); + + /** + * Removes a visrtual desktop from a window + * + * @since 5.46 + */ + void removePlasmaVirtualDesktop(const QString &id); + + /** + * The ids of all the desktops currently associated with this window. + * When a desktop is deleted it will be automatically removed from this list + * + * @since 5.46 + */ + QStringList plasmaVirtualDesktops() const; + Q_SIGNALS: void closeRequested(); /** @@ -191,6 +236,10 @@ * @since 5.22 */ void resizeRequested(); + /** + * DEPRECATED + * @see enterPlasmaVirtualDesktopRequested + */ void virtualDesktopRequested(quint32 desktop); void activeRequested(bool set); void minimizedRequested(bool set); @@ -222,10 +271,25 @@ */ void resizableRequested(bool set); /** + * DEPRECATED * @since 5.22 */ void virtualDesktopChangeableRequested(bool set); + /** + * Emitted when the client wishes this window to enter in a new virtual desktop. + * The server will decide whether to consent this request + * @since 5.46 + */ + void enterPlasmaVirtualDesktopRequested(const QString &desktop); + + /** + * Emitted when the client wishes to remove this window from a virtual desktop. + * The server will decide whether to consent this request + * @since 5.46 + */ + void leavePlasmaVirtualDesktopRequested(const QString &desktop); + private: friend class PlasmaWindowManagementInterface; explicit PlasmaWindowInterface(PlasmaWindowManagementInterface *wm, QObject *parent); diff --git a/src/server/plasmawindowmanagement_interface.cpp b/src/server/plasmawindowmanagement_interface.cpp --- a/src/server/plasmawindowmanagement_interface.cpp +++ b/src/server/plasmawindowmanagement_interface.cpp @@ -22,6 +22,7 @@ #include "resource_p.h" #include "display.h" #include "surface_interface.h" +#include "plasmavirtualdesktop_interface.h" #include #include @@ -48,6 +49,7 @@ ShowingDesktopState state = ShowingDesktopState::Disabled; QVector resources; QList windows; + QPointer plasmaVirtualDesktopManagementInterface = nullptr; quint32 windowIdCounter = 0; private: @@ -90,6 +92,7 @@ bool unmapped = false; PlasmaWindowInterface *parentWindow = nullptr; QMetaObject::Connection parentWindowDestroyConnection; + QStringList plasmaVirtualDesktops; QRect geometry; private: @@ -103,6 +106,8 @@ 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 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)); } @@ -119,7 +124,7 @@ static const struct org_kde_plasma_window_interface s_interface; }; -const quint32 PlasmaWindowManagementInterface::Private::s_version = 7; +const quint32 PlasmaWindowManagementInterface::Private::s_version = 8; PlasmaWindowManagementInterface::Private::Private(PlasmaWindowManagementInterface *q, Display *d) : Global::Private(d, &org_kde_plasma_window_management_interface, s_version) @@ -255,6 +260,12 @@ return window; } +QList PlasmaWindowManagementInterface::windows() const +{ + Q_D(); + return d->windows; +} + void PlasmaWindowManagementInterface::unmapWindow(PlasmaWindowInterface *window) { if (!window) { @@ -266,6 +277,21 @@ 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, @@ -276,7 +302,9 @@ requestMoveCallback, requestResizeCallback, destroyCallback, - getIconCallback + getIconCallback, + requestEnterVirtualDesktopCallback, + requestLeaveVirtualDesktopCallback }; #endif @@ -425,6 +453,20 @@ ); } +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::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) { @@ -773,6 +815,47 @@ d->setIcon(icon); } +void PlasmaWindowInterface::addPlasmaVirtualDesktop(const QString &id) +{ + //don't add a desktop we're not sure it exists + if (!d->wm->plasmaVirtualDesktopManagementInterface() || d->plasmaVirtualDesktops.contains(id)) { + return; + } + + PlasmaVirtualDesktopInterface *desktop = d->wm->plasmaVirtualDesktopManagementInterface()->desktop(id); + + if (!desktop) { + return; + } + + d->plasmaVirtualDesktops << id; + + //if the desktop dies, remove it from or list + connect(desktop, &QObject::destroyed, + this, [this, id](){removePlasmaVirtualDesktop(id);}); + + for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { + org_kde_plasma_window_send_virtual_desktop_entered(*it, id.toUtf8().constData()); + } +} + +void PlasmaWindowInterface::removePlasmaVirtualDesktop(const QString &id) +{ + if (!d->plasmaVirtualDesktops.contains(id)) { + return; + } + + d->plasmaVirtualDesktops.removeAll(id); + for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { + org_kde_plasma_window_send_virtual_desktop_left(*it, id.toUtf8().constData()); + } +} + +QStringList PlasmaWindowInterface::plasmaVirtualDesktops() const +{ + return d->plasmaVirtualDesktops; +} + void PlasmaWindowInterface::setShadeable(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADEABLE, set); diff --git a/src/tools/mapping.txt b/src/tools/mapping.txt --- a/src/tools/mapping.txt +++ b/src/tools/mapping.txt @@ -36,6 +36,8 @@ org_kde_kwin_shadow_manager;ShadowManager org_kde_plasma_shell;PlasmaShell org_kde_plasma_surface;PlasmaShellSurface +org_kde_plasma_virtual_desktop_management;PlasmaVirtualDesktopManagement +org_kde_plasma_virtual_desktop;PlasmaVirtualDesktop org_kde_plasma_window_management;PlasmaWindowManagement org_kde_plasma_window;PlasmaWindow org_kde_kwin_server_decoration_manager;ServerSideDecorationManager