diff --git a/.arclint b/.arclint new file mode 100644 --- /dev/null +++ b/.arclint @@ -0,0 +1,20 @@ +{ + "exclude": "(^test/)", + "linters": { + "spelling": { + "type": "spelling" + }, + "merge-conflict": { + "type": "merge-conflict" + }, + "xml": { + "type": "xml", + "include": "(\\.xml$)" + }, + "cppcheck": { + "type": "cppcheck", + "include": "(\\.(cpp|h|cxx|hpp)$)", + "flags": ["--language=c++", "--std=c++11"] + } + } +} diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,11 +1,11 @@ cmake_minimum_required(VERSION 3.0) -set(KF5_VERSION "5.46.0") # handled by release scripts +set(KF5_VERSION "5.48.0") # handled by release scripts project(KWayland VERSION ${KF5_VERSION}) # ECM setup include(FeatureSummary) -find_package(ECM 5.46.0 NO_MODULE) +find_package(ECM 5.47.0 NO_MODULE) set_package_properties(ECM PROPERTIES TYPE REQUIRED DESCRIPTION "Extra CMake Modules." URL "https://projects.kde.org/projects/kdesupport/extra-cmake-modules") feature_summary(WHAT REQUIRED_PACKAGES_NOT_FOUND FATAL_ON_MISSING_REQUIRED_PACKAGES) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR}) @@ -59,8 +59,11 @@ ecm_install_po_files_as_qm(po) endif() add_subdirectory(src) -add_subdirectory(autotests) -add_subdirectory(tests) + +if (BUILD_TESTING) + add_subdirectory(autotests) + add_subdirectory(tests) +endif() # create a Config.cmake and a ConfigVersion.cmake file and install them set(CMAKECONFIG_INSTALL_DIR "${CMAKECONFIG_INSTALL_PREFIX}/KF5Wayland") 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,26 @@ 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) + +######################################################## +# Test XDG Output +######################################################## +set( testXdgOutput_SRCS + test_xdg_output.cpp + ) +add_executable(testXdgOutput ${testXdgOutput_SRCS}) +target_link_libraries( testXdgOutput Qt5::Test Qt5::Gui KF5::WaylandClient KF5::WaylandServer Wayland::Client Wayland::Server) +add_test(NAME kwayland-testXdgOutput COMMAND testXdgOutput) +ecm_mark_as_test(testXdgOutput) + diff --git a/autotests/client/test_datadevice.cpp b/autotests/client/test_datadevice.cpp --- a/autotests/client/test_datadevice.cpp +++ b/autotests/client/test_datadevice.cpp @@ -512,6 +512,12 @@ QCOMPARE(selectionOfferedSpy.count(), 2); QVERIFY(sourceCancelled2Spy.isEmpty()); + // replace the data source with itself, ensure that it did not get cancelled + dataDevice->setSelection(1, dataSource2.data()); + QVERIFY(!sourceCancelled2Spy.wait(500)); + QCOMPARE(selectionOfferedSpy.count(), 2); + QVERIFY(sourceCancelled2Spy.isEmpty()); + // create a new DataDevice and replace previous one QScopedPointer dataDevice2(m_dataDeviceManager->getDataDevice(m_seat)); QVERIFY(dataDevice2->isValid()); 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,547 @@ +/******************************************************************** +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_unstable_v1.h" +#include "../../src/server/display.h" +#include "../../src/server/compositor_interface.h" +#include "../../src/server/region_interface.h" +#include "../../src/server/plasmavirtualdesktop_interface_unstable_v1.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::PlasmaVirtualDesktopManagementV1Interface *m_plasmaVirtualDesktopManagementInterface; + KWayland::Server::PlasmaWindowManagementInterface *m_windowManagementInterface; + KWayland::Server::PlasmaWindowInterface *m_windowInterface; + + KWayland::Client::ConnectionThread *m_connection; + KWayland::Client::Compositor *m_compositor; + KWayland::Client::PlasmaVirtualDesktopManagementUnstableV1 *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::plasmaVirtualDesktopManagementUnstableV1Announced); + 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.createPlasmaVirtualDesktopManagementUnstableV1(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->setPlasmaVirtualDesktopManagementInterfaceUnstableV1(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 desktopCreatedSpy(m_plasmaVirtualDesktopManagement, &PlasmaVirtualDesktopManagementUnstableV1::desktopCreated); + QSignalSpy managementDoneSpy(m_plasmaVirtualDesktopManagement, &PlasmaVirtualDesktopManagementUnstableV1::done); + + + //on this createDesktop bind() isn't called already, the desktopadded signals will be sent after bind happened + KWayland::Server::PlasmaVirtualDesktopV1Interface *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::PlasmaVirtualDesktopUnstableV1 *desktop1 = m_plasmaVirtualDesktopManagement->desktops().first(); + QSignalSpy desktop1DoneSpy(desktop1, &PlasmaVirtualDesktopUnstableV1::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::PlasmaVirtualDesktopV1Interface *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::PlasmaVirtualDesktopV1Interface *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::PlasmaVirtualDesktopUnstableV1 *desktop2 = m_plasmaVirtualDesktopManagement->desktops()[1]; + QSignalSpy desktop2DoneSpy(desktop2, &PlasmaVirtualDesktopUnstableV1::done); + desktop2Int->sendDone(); + desktop2DoneSpy.wait(); + + KWayland::Client::PlasmaVirtualDesktopUnstableV1 *desktop3 = m_plasmaVirtualDesktopManagement->desktops()[2]; + QSignalSpy desktop3DoneSpy(desktop3, &PlasmaVirtualDesktopUnstableV1::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::plasmaVirtualDesktopManagementUnstableV1Announced); + QVERIFY(plasmaVirtualDesktopManagementSpy.isValid()); + + QVERIFY(plasmaVirtualDesktopManagementSpy.wait()); + + KWayland::Client::PlasmaVirtualDesktopManagementUnstableV1 *otherPlasmaVirtualDesktopManagement = registry.createPlasmaVirtualDesktopManagementUnstableV1(plasmaVirtualDesktopManagementSpy.first().first().value(), plasmaVirtualDesktopManagementSpy.first().last().value(), this); + + QSignalSpy managementDoneSpy(otherPlasmaVirtualDesktopManagement, &PlasmaVirtualDesktopManagementUnstableV1::done); + + managementDoneSpy.wait(); + QCOMPARE(otherPlasmaVirtualDesktopManagement->desktops().length(), 3); + + delete otherPlasmaVirtualDesktopManagement; +} + +void TestVirtualDesktop::testDestroy() +{ + //rebuild some desktops + testCreate(); + + KWayland::Server::PlasmaVirtualDesktopV1Interface *desktop1Int = m_plasmaVirtualDesktopManagementInterface->desktops().first(); + KWayland::Client::PlasmaVirtualDesktopUnstableV1 *desktop1 = m_plasmaVirtualDesktopManagement->desktops().first(); + + + QSignalSpy desktop1IntDestroyedSpy(desktop1Int, &QObject::destroyed); + QSignalSpy desktop1DestroyedSpy(desktop1, &QObject::destroyed); + QSignalSpy desktop1RemovedSpy(desktop1, &KWayland::Client::PlasmaVirtualDesktopUnstableV1::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::PlasmaVirtualDesktopManagementUnstableV1::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::PlasmaVirtualDesktopV1Interface *desktop1Int = m_plasmaVirtualDesktopManagementInterface->desktops().first(); + KWayland::Client::PlasmaVirtualDesktopUnstableV1 *desktop1 = m_plasmaVirtualDesktopManagement->desktops().first(); + QVERIFY(desktop1->isActive()); + QVERIFY(desktop1Int->isActive()); + + KWayland::Server::PlasmaVirtualDesktopV1Interface *desktop2Int = m_plasmaVirtualDesktopManagementInterface->desktops()[1]; + KWayland::Client::PlasmaVirtualDesktopUnstableV1 *desktop2 = m_plasmaVirtualDesktopManagement->desktops()[1]; + QVERIFY(!desktop2Int->isActive()); + + QSignalSpy requestActivateSpy(desktop2Int, &KWayland::Server::PlasmaVirtualDesktopV1Interface::activateRequested); + QSignalSpy activatedSpy(desktop2, &KWayland::Client::PlasmaVirtualDesktopUnstableV1::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::PlasmaVirtualDesktopUnstableV1::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()); + + //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::PlasmaVirtualDesktopManagementV1Interface::desktopCreateRequested); + QSignalSpy desktopCreatedSpy(m_plasmaVirtualDesktopManagement, &PlasmaVirtualDesktopManagementUnstableV1::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(), 1); + + //actually create + m_plasmaVirtualDesktopManagementInterface->createDesktop(QStringLiteral("0-4"), 1); + KWayland::Server::PlasmaVirtualDesktopV1Interface *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); + + PlasmaVirtualDesktopUnstableV1 *desktop = m_plasmaVirtualDesktopManagement->desktops().at(1); + QSignalSpy desktopDoneSpy(desktop, &PlasmaVirtualDesktopUnstableV1::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::PlasmaVirtualDesktopManagementV1Interface::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 --- a/autotests/client/test_plasma_window_model.cpp +++ b/autotests/client/test_plasma_window_model.cpp @@ -70,6 +70,7 @@ void testIsOnAllDesktops(); void testIsDemandingAttention(); void testSkipTaskbar(); + void testSkipSwitcher(); void testIsShadeable(); void testIsShaded(); void testIsMovable(); @@ -235,6 +236,7 @@ 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"); @@ -414,6 +416,11 @@ QVERIFY(testBooleanData(PlasmaWindowModel::SkipTaskbar, &PlasmaWindowInterface::setSkipTaskbar)); } +void PlasmaWindowModelTest::testSkipSwitcher() +{ + QVERIFY(testBooleanData(PlasmaWindowModel::SkipSwitcher, &PlasmaWindowInterface::setSkipSwitcher)); +} + void PlasmaWindowModelTest::testIsShadeable() { QVERIFY(testBooleanData(PlasmaWindowModel::IsShadeable, &PlasmaWindowInterface::setShadeable)); diff --git a/autotests/client/test_plasmashell.cpp b/autotests/client/test_plasmashell.cpp --- a/autotests/client/test_plasmashell.cpp +++ b/autotests/client/test_plasmashell.cpp @@ -47,6 +47,7 @@ void testRole(); void testPosition(); void testSkipTaskbar(); + void testSkipSwitcher(); void testPanelBehavior_data(); void testPanelBehavior(); void testAutoHidePanel(); @@ -297,6 +298,41 @@ QVERIFY(!sps->skipTaskbar()); } +void TestPlasmaShell::testSkipSwitcher() +{ + // this test verifies that Skip Switcher is properly passed to server + QSignalSpy plasmaSurfaceCreatedSpy(m_plasmaShellInterface, &PlasmaShellInterface::surfaceCreated); + QVERIFY(plasmaSurfaceCreatedSpy.isValid()); + + QScopedPointer s(m_compositor->createSurface()); + QScopedPointer ps(m_plasmaShell->createSurface(s.data())); + QVERIFY(plasmaSurfaceCreatedSpy.wait()); + QCOMPARE(plasmaSurfaceCreatedSpy.count(), 1); + + // verify that we got a plasma shell surface + auto sps = plasmaSurfaceCreatedSpy.first().first().value(); + QVERIFY(sps); + QVERIFY(sps->surface()); + QVERIFY(!sps->skipSwitcher()); + + // now change + QSignalSpy skipSwitcherChangedSpy(sps, &PlasmaShellSurfaceInterface::skipSwitcherChanged); + QVERIFY(skipSwitcherChangedSpy.isValid()); + ps->setSkipSwitcher(true); + QVERIFY(skipSwitcherChangedSpy.wait()); + QVERIFY(sps->skipSwitcher()); + // setting to same again should not emit the signal + ps->setSkipSwitcher(true); + QEXPECT_FAIL("", "Should not be emitted if not changed", Continue); + QVERIFY(!skipSwitcherChangedSpy.wait(100)); + QVERIFY(sps->skipSwitcher()); + + // setting to false should change again + ps->setSkipSwitcher(false); + QVERIFY(skipSwitcherChangedSpy.wait()); + QVERIFY(!sps->skipSwitcher()); +} + void TestPlasmaShell::testPanelBehavior_data() { QTest::addColumn("client"); diff --git a/autotests/client/test_wayland_registry.cpp b/autotests/client/test_wayland_registry.cpp --- a/autotests/client/test_wayland_registry.cpp +++ b/autotests/client/test_wayland_registry.cpp @@ -598,7 +598,7 @@ //server removes the global //(simultaneously) the client legimitely uses the bound resource to the global - //client then gets the server events...it should no-op, not be a protcol error + //client then gets the server events...it should no-op, not be a protocol error using namespace KWayland::Client; KWayland::Client::ConnectionThread connection; diff --git a/autotests/client/test_wayland_surface.cpp b/autotests/client/test_wayland_surface.cpp --- a/autotests/client/test_wayland_surface.cpp +++ b/autotests/client/test_wayland_surface.cpp @@ -71,12 +71,12 @@ private: KWayland::Server::Display *m_display; KWayland::Server::CompositorInterface *m_compositorInterface; - KWayland::Server::IdleInhibitManagerInterface *m_idleInhibitInterface = nullptr; + KWayland::Server::IdleInhibitManagerInterface *m_idleInhibitInterface; KWayland::Client::ConnectionThread *m_connection; KWayland::Client::Compositor *m_compositor; KWayland::Client::ShmPool *m_shm; KWayland::Client::EventQueue *m_queue; - KWayland::Client::IdleInhibitManager *m_idleInhibitManager = nullptr; + KWayland::Client::IdleInhibitManager *m_idleInhibitManager; QThread *m_thread; }; @@ -190,6 +190,9 @@ delete m_compositorInterface; m_compositorInterface = nullptr; + delete m_idleInhibitInterface; + m_idleInhibitInterface = nullptr; + delete m_display; m_display = nullptr; } @@ -779,13 +782,19 @@ connect(m_connection, &ConnectionThread::connectionDied, m_compositor, &Compositor::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_shm, &ShmPool::destroy); connect(m_connection, &ConnectionThread::connectionDied, m_queue, &EventQueue::destroy); + connect(m_connection, &ConnectionThread::connectionDied, m_idleInhibitManager, &IdleInhibitManager::destroy); QVERIFY(s->isValid()); QSignalSpy connectionDiedSpy(m_connection, SIGNAL(connectionDied())); QVERIFY(connectionDiedSpy.isValid()); + + delete m_compositorInterface; + m_compositorInterface = nullptr; + delete m_idleInhibitInterface; + m_idleInhibitInterface = nullptr; delete m_display; m_display = nullptr; - m_compositorInterface = nullptr; + QVERIFY(connectionDiedSpy.wait()); // now the Surface should be destroyed; diff --git a/autotests/client/test_wayland_windowmanagement.cpp b/autotests/client/test_wayland_windowmanagement.cpp --- a/autotests/client/test_wayland_windowmanagement.cpp +++ b/autotests/client/test_wayland_windowmanagement.cpp @@ -407,6 +407,7 @@ QTest::newRow("maximizable") << &PlasmaWindowInterface::maximizeableRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZABLE); QTest::newRow("fullscreenable") << &PlasmaWindowInterface::fullscreenableRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREENABLE); QTest::newRow("skiptaskbar") << &PlasmaWindowInterface::skipTaskbarRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPTASKBAR); + QTest::newRow("skipSwitcher") << &PlasmaWindowInterface::skipSwitcherRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPSWITCHER); QTest::newRow("shadeable") << &PlasmaWindowInterface::shadeableRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADEABLE); QTest::newRow("shaded") << &PlasmaWindowInterface::shadedRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED); QTest::newRow("movable") << &PlasmaWindowInterface::movableRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MOVABLE); diff --git a/autotests/client/test_xdg_output.cpp b/autotests/client/test_xdg_output.cpp new file mode 100644 --- /dev/null +++ b/autotests/client/test_xdg_output.cpp @@ -0,0 +1,174 @@ +/******************************************************************** +Copyright 2018 David Edmundson + +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/connection_thread.h" +#include "../../src/client/event_queue.h" +#include "../../src/client/dpms.h" +#include "../../src/client/output.h" +#include "../../src/client/xdgoutput.h" +#include "../../src/client/registry.h" +#include "../../src/server/display.h" +#include "../../src/server/dpms_interface.h" +#include "../../src/server/output_interface.h" +#include "../../src/server/xdgoutput_interface.h" + +// Wayland + +class TestXdgOutput : public QObject +{ + Q_OBJECT +public: + explicit TestXdgOutput(QObject *parent = nullptr); +private Q_SLOTS: + void init(); + void cleanup(); + void testChanges(); +private: + KWayland::Server::Display *m_display; + KWayland::Server::OutputInterface *m_serverOutput; + KWayland::Server::XdgOutputManagerInterface *m_serverXdgOutputManager; + KWayland::Server::XdgOutputInterface *m_serverXdgOutput; + KWayland::Client::ConnectionThread *m_connection; + KWayland::Client::EventQueue *m_queue; + QThread *m_thread; +}; + +static const QString s_socketName = QStringLiteral("kwin-test-xdg-output-0"); + +TestXdgOutput::TestXdgOutput(QObject *parent) + : QObject(parent) + , m_display(nullptr) + , m_serverOutput(nullptr) + , m_connection(nullptr) + , m_thread(nullptr) +{ +} + +void TestXdgOutput::init() +{ + using namespace KWayland::Server; + delete m_display; + m_display = new Display(this); + m_display->setSocketName(s_socketName); + m_display->start(); + QVERIFY(m_display->isRunning()); + + m_serverOutput = m_display->createOutput(this); + m_serverOutput->addMode(QSize(1920, 1080), OutputInterface::ModeFlags(OutputInterface::ModeFlag::Preferred)); + m_serverOutput->setCurrentMode(QSize(1920, 1080)); + m_serverOutput->create(); + + m_serverXdgOutputManager = m_display->createXdgOutputManager(this); + m_serverXdgOutputManager->create(); + m_serverXdgOutput = m_serverXdgOutputManager->createXdgOutput(m_serverOutput, this); + m_serverXdgOutput->setLogicalSize(QSize(1280, 720)); //a 1.5 scale factor + m_serverXdgOutput->setLogicalPosition(QPoint(11,12)); //not a sensible value for one monitor, but works for this test + m_serverXdgOutput->done(); + + // setup connection + m_connection = new KWayland::Client::ConnectionThread; + QSignalSpy connectedSpy(m_connection, SIGNAL(connected())); + m_connection->setSocketName(s_socketName); + + m_thread = new QThread(this); + m_connection->moveToThread(m_thread); + m_thread->start(); + + m_connection->initConnection(); + QVERIFY(connectedSpy.wait()); + + m_queue = new KWayland::Client::EventQueue(this); + QVERIFY(!m_queue->isValid()); + m_queue->setup(m_connection); + QVERIFY(m_queue->isValid()); +} + +void TestXdgOutput::cleanup() +{ + if (m_queue) { + delete m_queue; + m_queue = nullptr; + } + if (m_thread) { + m_thread->quit(); + m_thread->wait(); + delete m_thread; + m_thread = nullptr; + } + delete m_connection; + m_connection = nullptr; + + delete m_serverOutput; + m_serverOutput = nullptr; + + delete m_display; + m_display = nullptr; +} + +void TestXdgOutput::testChanges() +{ + // verify the server modes + using namespace KWayland::Server; + using namespace KWayland::Client; + KWayland::Client::Registry registry; + QSignalSpy announced(®istry, SIGNAL(outputAnnounced(quint32,quint32))); + QSignalSpy xdgOutputAnnounced(®istry, SIGNAL(xdgOutputAnnounced(quint32,quint32))); + + registry.setEventQueue(m_queue); + registry.create(m_connection->display()); + QVERIFY(registry.isValid()); + registry.setup(); + QVERIFY(announced.wait()); + if (xdgOutputAnnounced.count() != 1) { + QVERIFY(xdgOutputAnnounced.wait()); + } + + KWayland::Client::Output output; + QSignalSpy outputChanged(&output, SIGNAL(changed())); + + output.setup(registry.bindOutput(announced.first().first().value(), announced.first().last().value())); + QVERIFY(outputChanged.wait()); + + QScopedPointer xdgOutputManager(registry.createXdgOutputManager(xdgOutputAnnounced.first().first().value(), xdgOutputAnnounced.first().last().value(), this)); + + QScopedPointer xdgOutput(xdgOutputManager->getXdgOutput(&output, this)); + QSignalSpy xdgOutputChanged(xdgOutput.data(), SIGNAL(changed())); + + //check details are sent on client bind + QVERIFY(xdgOutputChanged.wait()); + xdgOutputChanged.clear(); + QCOMPARE(xdgOutput->logicalPosition(), QPoint(11,12)); + QCOMPARE(xdgOutput->logicalSize(), QSize(1280,720)); + + //dynamic updates + m_serverXdgOutput->setLogicalPosition(QPoint(1000, 2000)); + m_serverXdgOutput->setLogicalSize(QSize(100,200)); + m_serverXdgOutput->done(); + + QVERIFY(xdgOutputChanged.wait()); + QCOMPARE(xdgOutputChanged.count(), 1); + QCOMPARE(xdgOutput->logicalPosition(), QPoint(1000, 2000)); + QCOMPARE(xdgOutput->logicalSize(), QSize(100,200)); +} + +QTEST_GUILESS_MAIN(TestXdgOutput) +#include "test_xdg_output.moc" diff --git a/autotests/client/test_xdg_shell_v6.cpp b/autotests/client/test_xdg_shell_v6.cpp --- a/autotests/client/test_xdg_shell_v6.cpp +++ b/autotests/client/test_xdg_shell_v6.cpp @@ -68,7 +68,7 @@ void XdgShellTestV6::testPopup_data() { - QTest::addColumn("positioners"); + QTest::addColumn("positioner"); XdgPositioner positioner(QSize(10,10), QRect(100,100,50,50)); QTest::newRow("default") << positioner; @@ -105,7 +105,7 @@ QVERIFY(xdgTopLevelCreatedSpy.wait()); auto serverXdgTopLevel = xdgTopLevelCreatedSpy.first().first().value(); - XdgPositioner positioner(QSize(10,10), QRect(100,100,50,50)); + QFETCH(XdgPositioner, positioner); QScopedPointer surface(m_compositor->createSurface()); QScopedPointer xdgSurface(m_xdgShell->createPopup(surface.data(), xdgParentSurface.data(), positioner)); 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_unstable_v1.cpp plasmawindowmanagement.cpp plasmawindowmodel.cpp region.cpp @@ -57,6 +58,7 @@ xdgforeign_v2.cpp xdgforeign.cpp xdgshell_v6.cpp + xdgoutput.cpp ) ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS @@ -78,6 +80,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-unstable-v1.xml + BASENAME plasma-virtual-desktop-unstable-v1 +) + ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/plasma-window-management.xml BASENAME plasma-window-management @@ -166,11 +174,17 @@ BASENAME server-decoration-palette ) +ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS + PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/xdg-output-unstable-v1.xml + BASENAME xdg-output-unstable-v1 +) + set(CLIENT_GENERATED_FILES ${CMAKE_CURRENT_BINARY_DIR}/wayland-fullscreen-shell-client-protocol.h ${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 @@ -190,6 +204,7 @@ ${CMAKE_CURRENT_BINARY_DIR}/wayland-pointer-constraints-unstable-v1-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-xdg-foreign-unstable-v2-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-idle-inhibit-unstable-v1-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-xdg-output-unstable-v1-client-protocol.h ) set_source_files_properties(${CLIENT_GENERATED_FILES} PROPERTIES SKIP_AUTOMOC ON) @@ -250,6 +265,7 @@ pointer.h pointerconstraints.h plasmashell.h + plasmavirtualdesktop_unstable_v1.h plasmawindowmanagement.h plasmawindowmodel.h pointergestures.h @@ -270,6 +286,7 @@ textinput.h xdgshell.h xdgforeign_v2.h + xdgoutput.h ) install(FILES diff --git a/src/client/plasmashell.h b/src/client/plasmashell.h --- a/src/client/plasmashell.h +++ b/src/client/plasmashell.h @@ -288,6 +288,13 @@ void setSkipTaskbar(bool skip); /** + * Setting this bit on a window will indicate it does not prefer + * to be included in a window switcher. + * @since 5.47 + */ + void setSkipSwitcher(bool skip); + + /** * Requests to hide a surface with Role Panel and PanelBahvior AutoHide. * * Once the compositor has hidden the panel the signal {@link autoHidePanelHidden} gets diff --git a/src/client/plasmashell.cpp b/src/client/plasmashell.cpp --- a/src/client/plasmashell.cpp +++ b/src/client/plasmashell.cpp @@ -322,6 +322,11 @@ org_kde_plasma_surface_set_skip_taskbar(d->surface, skip); } +void PlasmaShellSurface::setSkipSwitcher(bool skip) +{ + org_kde_plasma_surface_set_skip_switcher(d->surface, skip); +} + void PlasmaShellSurface::requestHideAutoHidingPanel() { org_kde_plasma_surface_panel_auto_hide_hide(d->surface); 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 PlasmaVirtualDesktopUnstableV1; /** * @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 **/ @@ -343,6 +346,11 @@ **/ bool skipTaskbar() const; /** + * @returns Whether the window should be ignored by a switcher. + * @see skipSwitcherChanged + **/ + bool skipSwitcher() const; + /** * @returns The icon of the window. * @see iconChanged **/ @@ -404,6 +412,7 @@ */ void requestResize(); /** + * @deprecated: use requestEnterVirtualDesktop instead * Requests to send the window to virtual @p desktop. **/ void requestVirtualDesktop(quint32 desktop); @@ -475,6 +484,40 @@ **/ 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); + + /** + * RFC: do this with an empty id to request_enter_virtual_desktop? + * Make the window enter a new virtual desktop. If the server consents the request, + * it will create a new virtual desktop and assign the window to it. + */ + void requestEnterNewVirtualDesktop(); + + /** + * 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,8 +530,8 @@ **/ void appIdChanged(); /** + * @deprecated use plasmaVirtualDesktopEntered and plasmaVirtualDesktopLeft instead * The virtual desktop changed. - * @see virtualDesktop **/ void virtualDesktopChanged(); /** @@ -557,6 +600,11 @@ **/ void skipTaskbarChanged(); /** + * The skip switcher state changed. + * @see skipSwitcher + **/ + void skipSwitcherChanged(); + /** * The window icon changed. * @see icon **/ @@ -610,6 +658,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_unstable_v1.h" #include "event_queue.h" #include "output.h" #include "surface.h" @@ -82,6 +83,7 @@ bool maximizeable = false; bool fullscreenable = false; bool skipTaskbar = false; + bool skipSwitcher = false; bool shadeable = false; bool shaded = false; bool movable = false; @@ -92,6 +94,7 @@ bool unmapped = false; QPointer parentWindow; QMetaObject::Connection parentWindowUnmappedConnection; + QStringList plasmaVirtualDesktops; QRect geometry; quint32 pid = 0; @@ -107,6 +110,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); @@ -120,6 +125,7 @@ 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); @@ -345,7 +351,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 +464,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); @@ -473,6 +499,7 @@ 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); @@ -671,6 +698,15 @@ 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) { @@ -812,7 +848,13 @@ bool PlasmaWindow::isOnAllDesktops() const { - return d->onAllDesktops; + //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 @@ -845,6 +887,10 @@ return d->skipTaskbar; } +bool PlasmaWindow::skipSwitcher() const +{ + return d->skipSwitcher; +} QIcon PlasmaWindow::icon() const { @@ -993,5 +1039,25 @@ 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.h b/src/client/plasmawindowmodel.h --- a/src/client/plasmawindowmodel.h +++ b/src/client/plasmawindowmodel.h @@ -105,22 +105,26 @@ /** * @since 5.35 */ - Pid + Pid, + /** + * @since 5.47 + */ + SkipSwitcher, }; Q_ENUM(AdditionalRoles) explicit PlasmaWindowModel(PlasmaWindowManagement *parent); virtual ~PlasmaWindowModel(); - QHash roleNames() const Q_DECL_OVERRIDE; + QHash roleNames() const override; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE; - int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_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 Q_DECL_OVERRIDE; + QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const override; /** diff --git a/src/client/plasmawindowmodel.cpp b/src/client/plasmawindowmodel.cpp --- a/src/client/plasmawindowmodel.cpp +++ b/src/client/plasmawindowmodel.cpp @@ -133,6 +133,10 @@ [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); } ); @@ -253,6 +257,8 @@ 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) { diff --git a/src/client/protocols/fullscreen-shell.xml b/src/client/protocols/fullscreen-shell.xml --- a/src/client/protocols/fullscreen-shell.xml +++ b/src/client/protocols/fullscreen-shell.xml @@ -62,7 +62,7 @@ its own cursor and set wl_pointer.cursor(NULL). - + @@ -74,7 +74,7 @@ the wl_fullscreen_shell.capability enum. If clients want to take advantage of any of these capabilities, they sould use a wl_display.sync request immediatly after binding to ensure that they - recieve all the capability events. + receive all the capability events. diff --git a/src/client/protocols/outputdevice.xml b/src/client/protocols/outputdevice.xml --- a/src/client/protocols/outputdevice.xml +++ b/src/client/protocols/outputdevice.xml @@ -223,7 +223,7 @@ The uuid can be used to identify the output. It's controlled by the server entirely. The server should make sure the uuid is - persistant across restarts. An empty uuid is considered invalid. + persistent across restarts. An empty uuid is considered invalid. diff --git a/src/client/protocols/plasma-shell.xml b/src/client/protocols/plasma-shell.xml --- a/src/client/protocols/plasma-shell.xml +++ b/src/client/protocols/plasma-shell.xml @@ -17,7 +17,7 @@ along with this program. If not, see . ]]> - + This interface is used by KF5 powered Wayland shells to communicate with the compositor and can only be bound one time. @@ -130,7 +130,7 @@ --> - + An interface that may be implemented by a wl_surface, for implementations that provide the shell user interface. @@ -332,7 +332,7 @@ Setting this bit to the window, will make it say it prefers to not be listed in the taskbar. Taskbar implementations may or may not follow this hint. - + @@ -384,5 +384,14 @@ An auto-hiding panel got shown by the compositor. + + + + + Setting this bit will indicate that the window prefers not to be listed in a switcher. + + + + 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 @@ -17,7 +17,7 @@ along with this program. If not, see . ]]> - + This interface manages application windows. It provides requests to show and hide the desktop and emits @@ -46,6 +46,7 @@ + @@ -83,7 +84,7 @@ - + Manages and control an application window. @@ -104,6 +105,7 @@ + Deprecated: use enter_virtual_desktop Maps the window to a different virtual desktop. To show the window on all virtual desktops, call the @@ -198,6 +200,7 @@ + DEPRECATED: use virtual_desktop_entered and virtual_desktop_left instead This event will be sent when a window is moved to another virtual desktop. @@ -266,5 +269,45 @@ + + + + + + Make the window enter a virtual desktop. A window can enter more + than one virtual desktop. if the id is empty or invalid, no action will be performed. + + + + + + RFC: do this with an empty id to request_enter_virtual_desktop? + Make the window enter a new virtual desktop. If the server consents the request, + it will create a new virtual desktop and assign the window to it. + + + + + + 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. + If the window gets manually added on all desktops, the server has to send virtual_desktop_left for every previous desktop it was in for the window to be really considered on all desktops. + + + + diff --git a/src/client/protocols/xdg-output-unstable-v1.xml b/src/client/protocols/xdg-output-unstable-v1.xml new file mode 100644 --- /dev/null +++ b/src/client/protocols/xdg-output-unstable-v1.xml @@ -0,0 +1,161 @@ + + + + + Copyright © 2017 Red Hat Inc. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + This protocol aims at describing outputs in a way which is more in line + with the concept of an output on desktop oriented systems. + + Some information are more specific to the concept of an output for + a desktop oriented system and may not make sense in other applications, + such as IVI systems for example. + + Typically, the global compositor space on a desktop system is made of + a contiguous or overlapping set of rectangular regions. + + Some of the information provided in this protocol might be identical + to their counterparts already available from wl_output, in which case + the information provided by this protocol should be preferred to their + equivalent in wl_output. The goal is to move the desktop specific + concepts (such as output location within the global compositor space, + the connector name and types, etc.) out of the core wl_output protocol. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible + changes may be added together with the corresponding interface + version bump. + Backward incompatible changes are done by bumping the version + number in the protocol and interface names and resetting the + interface version. Once the protocol is to be declared stable, + the 'z' prefix and the version number in the protocol and + interface names are removed and the interface version number is + reset. + + + + + A global factory interface for xdg_output objects. + + + + + Using this request a client can tell the server that it is not + going to use the xdg_output_manager object anymore. + + Any objects already created through this instance are not affected. + + + + + + This creates a new xdg_output object for the given wl_output. + + + + + + + + + An xdg_output describes part of the compositor geometry. + + This typically corresponds to a monitor that displays part of the + compositor space. + + + + + Using this request a client can tell the server that it is not + going to use the xdg_output object anymore. + + + + + + The position event describes the location of the wl_output within + the global compositor space. + + The logical_position event is sent after creating an xdg_output + (see xdg_output_manager.get_xdg_output) and whenever the location + of the output changes within the global compositor space. + + + + + + + + The logical_size event describes the size of the output in the + global compositor space. + + For example, a surface without any buffer scale, transformation + nor rotation set, with the size matching the logical_size will + have the same size as the corresponding output when displayed. + + Most regular Wayland clients should not pay attention to the + logical size and would rather rely on xdg_shell interfaces. + + Some clients such as Xwayland, however, need this to configure + their surfaces in the global compositor space as the compositor + may apply a different scale from what is advertised by the output + scaling property (to achieve fractional scaling, for example). + + For example, for a wl_output mode 3840×2160 and a scale factor 2: + + - A compositor not scaling the surface buffers will advertise a + logical size of 3840×2160, + + - A compositor automatically scaling the surface buffers will + advertise a logical size of 1920×1080, + + - A compositor using a fractional scale of 1.5 will advertise a + logical size to 2560×1620. + + The logical_size event is sent after creating an xdg_output + (see xdg_output_manager.get_xdg_output) and whenever the logical + size of the output changes, either as a result of a change in the + applied scale or because of a change in the corresponding output + mode(see wl_output.mode) or transform (see wl_output.transform). + + + + + + + + This event is sent after all other properties of an xdg_output + have been sent. + + This allows changes to the xdg_output properties to be seen as + atomic, even if they happen via multiple events. + + + + + 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_v1; struct org_kde_plasma_window_management; struct org_kde_kwin_server_decoration_manager; struct org_kde_kwin_server_decoration_palette_manager; @@ -60,6 +61,7 @@ struct zxdg_exporter_v2; struct zxdg_importer_v2; struct zwp_idle_inhibit_manager_v1; +struct zxdg_output_manager_v1; namespace KWayland { @@ -81,6 +83,7 @@ class RemoteAccessManager; class Output; class PlasmaShell; +class PlasmaVirtualDesktopManagementUnstableV1; class PlasmaWindowManagement; class PointerConstraints; class PointerGestures; @@ -103,6 +106,7 @@ class XdgImporterUnstableV2; class XdgExporter; class XdgImporter; +class XdgOutputManager; /** * @short Wrapper for the wl_registry interface. @@ -172,7 +176,9 @@ 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 + PlasmaVirtualDesktopManagementUnstableV1, ///< Refers to org_kde_plasma_virtual_desktop_management_v1 interface @since 5.47 + XdgOutputUnstableV1, ///refers to zxdg_output_v1, @since 5.47 }; explicit Registry(QObject *parent = nullptr); virtual ~Registry(); @@ -221,7 +227,7 @@ * * The EventQueue should be set before the Registry gets setup. * The EventQueue gets automatically added to all interfaces created by - * this Registry. So that all objects are in teh same EventQueue. + * this Registry. So that all objects are in the same EventQueue. * * @param queue The event queue to use for this Registry. **/ @@ -388,13 +394,23 @@ **/ org_kde_plasma_shell *bindPlasmaShell(uint32_t name, uint32_t version) const; /** + * Binds the org_kde_plasma_virtual_desktop_management_v1 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_v1 *bindPlasmaVirtualDesktopManagementUnstableV1(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. * * Prefer using createPlasmaWindowManagement instead. * @see createPlasmaWindowManagement - * @since 5.4 + * @since 5.46 **/ org_kde_plasma_window_management *bindPlasmaWindowManagement(uint32_t name, uint32_t version) const; /** @@ -607,10 +623,22 @@ * @c null will be returned. * * Prefer using createServerSideDecorationPaletteManager instead. - * @see createAppMenuManager + * @see createServerSideDecorationPaletteManager * @since 5.42 **/ org_kde_kwin_server_decoration_palette_manager *bindServerSideDecorationPaletteManager(uint32_t name, uint32_t version) const; + + /** + * Binds the zxdg_output_v1 with @p name and @p version. + * If the @p name does not exist, + * @c null will be returned. + * + * Prefer using createXdgOutputManager instead. + * @see createXdgOutputManager + * @since 5.47 + **/ + zxdg_output_manager_v1 *bindXdgOutputUnstableV1(uint32_t name, uint32_t version) const; + ///@} /** @@ -787,6 +815,22 @@ **/ PlasmaShell *createPlasmaShell(quint32 name, quint32 version, QObject *parent = nullptr); /** + * Creates a PlasmaVirtualDesktopManagementUnstableV1 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_v1 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_v1 interface to bind + * @param version The version or the org_kde_plasma_virtual_desktop_management_v1 interface to use + * @param parent The parent for PlasmaShell + * + * @returns The created PlasmaShell. + * @since 5.46 + **/ + PlasmaVirtualDesktopManagementUnstableV1 *createPlasmaVirtualDesktopManagementUnstableV1(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. * @@ -1116,6 +1160,24 @@ * @since 5.42 **/ ServerSideDecorationPaletteManager *createServerSideDecorationPaletteManager(quint32 name, quint32 version, QObject *parent = nullptr); + + /** + * Creates an XdgOutputManager 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 zxdg_output_manager_v1 interface, + * the returned XdgOutputManager will not be valid. Therefore it's recommended to call + * isValid on the created instance. + * + * @param name The name of the zxdg_output_manager_v1 interface to bind + * @param version The version or the zxdg_output_manager_v1 interface to use + * @param parent The parent for XdgOuptutManager + * + * @returns The created XdgOuptutManager. + * @since 5.47 + **/ + XdgOutputManager *createXdgOutputManager(quint32 name, quint32 version, QObject *parent = nullptr); + ///@} @@ -1202,6 +1264,13 @@ **/ void plasmaShellAnnounced(quint32 name, quint32 version); /** + * Emitted whenever a org_kde_plasma_virtual_desktop_management_v1 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 plasmaVirtualDesktopManagementUnstableV1Announced(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 @@ -1362,6 +1431,14 @@ */ void serverSideDecorationPaletteManagerAnnounced(quint32 name, quint32 version); + /** + * Emitted whenever a zxdg_output_v1 interface gets announced. + * @param name The name for the announced interface + * @param version The maximum supported version of the announced interface + * @since 5.47 + */ + void xdgOutputAnnounced(quint32 name, quint32 version); + ///@} /** @@ -1427,6 +1504,12 @@ **/ void plasmaShellRemoved(quint32 name); /** + * Emitted whenever a org_kde_plasma_virtual_desktop_management_v1 interface gets removed. + * @param name The name for the removed interface + * @since 5.46 + **/ + void plasmaVirtualDesktopManagementUnstableV1Removed(quint32 name); + /** * Emitted whenever a org_kde_plasma_window_management interface gets removed. * @param name The name for the removed interface * @since 5.4 @@ -1564,6 +1647,13 @@ **/ void serverSideDecorationPaletteManagerRemoved(quint32 name); + /** + * Emitted whenever a zxdg_output_v1 gets removed. + * @param name The name of the removed interface + * @since 5.47 + **/ + void xdgOutputRemoved(quint32 name); + ///@} /** * Generic announced signal which gets emitted whenever an interface gets 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_unstable_v1.h" #include "plasmawindowmanagement.h" #include "pointerconstraints.h" #include "pointergestures.h" @@ -54,12 +55,14 @@ #include "xdgforeign_v2.h" #include "appmenu.h" #include "server_decoration_palette.h" +#include "xdgoutput.h" // Qt #include // wayland #include #include #include +#include #include #include #include @@ -83,6 +86,7 @@ #include #include #include +#include /***** * How to add another interface: @@ -161,14 +165,21 @@ &Registry::subCompositorRemoved }}, {Registry::Interface::PlasmaShell, { - 4, + 5, QByteArrayLiteral("org_kde_plasma_shell"), &org_kde_plasma_shell_interface, &Registry::plasmaShellAnnounced, &Registry::plasmaShellRemoved }}, + {Registry::Interface::PlasmaVirtualDesktopManagementUnstableV1, { + 1, + QByteArrayLiteral("org_kde_plasma_virtual_desktop_management_v1"), + &org_kde_plasma_virtual_desktop_management_v1_interface, + &Registry::plasmaVirtualDesktopManagementUnstableV1Announced, + &Registry::plasmaVirtualDesktopManagementUnstableV1Removed + }}, {Registry::Interface::PlasmaWindowManagement, { - 7, + 9, QByteArrayLiteral("org_kde_plasma_window_management"), &org_kde_plasma_window_management_interface, &Registry::plasmaWindowManagementAnnounced, @@ -341,6 +352,13 @@ &org_kde_kwin_server_decoration_palette_manager_interface, &Registry::serverSideDecorationPaletteManagerAnnounced, &Registry::serverSideDecorationPaletteManagerRemoved + }}, + {Registry::Interface::XdgOutputUnstableV1, { + 1, + QByteArrayLiteral("zxdg_output_manager_v1"), + &zxdg_output_manager_v1_interface, + &Registry::xdgOutputAnnounced, + &Registry::xdgOutputRemoved }} }; @@ -629,6 +647,7 @@ BIND(FullscreenShell, _wl_fullscreen_shell) BIND(DataDeviceManager, wl_data_device_manager) BIND(PlasmaShell, org_kde_plasma_shell) +BIND(PlasmaVirtualDesktopManagementUnstableV1, org_kde_plasma_virtual_desktop_management_v1) BIND(PlasmaWindowManagement, org_kde_plasma_window_management) BIND(Idle, org_kde_kwin_idle) BIND(RemoteAccessManager, org_kde_kwin_remote_access_manager) @@ -653,6 +672,7 @@ BIND2(DpmsManager, Dpms, org_kde_kwin_dpms_manager) BIND2(AppMenuManager, AppMenu, org_kde_kwin_appmenu_manager) BIND2(ServerSideDecorationPaletteManager, ServerSideDecorationPalette, org_kde_kwin_server_decoration_palette_manager) +BIND(XdgOutputUnstableV1, zxdg_output_manager_v1) #undef BIND #undef BIND2 @@ -690,6 +710,7 @@ CREATE(Output) CREATE(DataDeviceManager) CREATE(PlasmaShell) +CREATE(PlasmaVirtualDesktopManagementUnstableV1) CREATE(PlasmaWindowManagement) CREATE(Idle) CREATE(RemoteAccessManager) @@ -785,6 +806,16 @@ } } +XdgOutputManager *Registry::createXdgOutputManager(quint32 name, quint32 version, QObject *parent) +{ + switch(d->interfaceForName(name)) { + case Interface::XdgOutputUnstableV1: + return d->create(name, version, parent, &Registry::bindXdgOutputUnstableV1); + default: + return nullptr; + } +} + namespace { static const wl_interface *wlInterface(Registry::Interface interface) { diff --git a/src/client/textinput.h b/src/client/textinput.h --- a/src/client/textinput.h +++ b/src/client/textinput.h @@ -342,7 +342,7 @@ quint32 afterLength; }; /** - * @returns The lenght in bytes which should be deleted around the cursor position + * @returns The length in bytes which should be deleted around the cursor position * @see committed **/ DeleteSurroundingText deleteSurroundingText() const; diff --git a/src/client/textinput_v0.cpp b/src/client/textinput_v0.cpp --- a/src/client/textinput_v0.cpp +++ b/src/client/textinput_v0.cpp @@ -437,6 +437,7 @@ bool isValid() override; void setupV0(wl_text_input_manager *ti) override; TextInput *createTextInput(Seat *seat, QObject *parent = nullptr) override; + using TextInputManager::Private::operator zwp_text_input_manager_v2*; //overriding only one overload results in a compiler warning. This tells GCC we're doing it deliberately operator wl_text_input_manager*() override { return textinputmanagerunstablev0; } diff --git a/src/client/textinput_v2.cpp b/src/client/textinput_v2.cpp --- a/src/client/textinput_v2.cpp +++ b/src/client/textinput_v2.cpp @@ -461,6 +461,7 @@ bool isValid() override; void setupV2(zwp_text_input_manager_v2 *ti) override; TextInput *createTextInput(Seat *seat, QObject *parent = nullptr) override; + using TextInputManager::Private::operator wl_text_input_manager*; operator zwp_text_input_manager_v2*() override { return textinputmanagerunstablev2; } diff --git a/src/client/xdgoutput.h b/src/client/xdgoutput.h new file mode 100644 --- /dev/null +++ b/src/client/xdgoutput.h @@ -0,0 +1,229 @@ +/**************************************************************************** +Copyright 2018 David Edmundson + +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_XDGOUTPUT_H +#define KWAYLAND_CLIENT_XDGOUTPUT_H + +#include +#include +#include + +#include + +struct zxdg_output_manager_v1; +struct zxdg_output_v1; + +namespace KWayland +{ +namespace Client +{ + +class EventQueue; +class XdgOutput; +class Output; + +/** + * @short Wrapper for the zxdg_output_manager_v1 interface. + * + * This class provides a convenient wrapper for the zxdg_output_manager_v1 interface. + * + * This provides the logical size of the output. This is useful in case it doesn't match the + * pixelSize / outputScale. + * + * To use this class one needs to interact with the Registry. There are two + * possible ways to create the XdgOutputManager interface: + * @code + * XdgOutputManager *c = registry->createXdgOutputManager(name, version); + * @endcode + * + * This creates the XdgOutputManager and sets it up directly. As an alternative this + * can also be done in a more low level way: + * @code + * XdgOutputManager *c = new XdgOutputManager; + * c->setup(registry->bindXdgOutputManager(name, version)); + * @endcode + * + * The XdgOutputManager can be used as a drop-in replacement for any zxdg_output_manager_v1 + * pointer as it provides matching cast operators. + * + * @since 5.47 + * + * @see Registry + **/ +class KWAYLANDCLIENT_EXPORT XdgOutputManager : public QObject +{ + Q_OBJECT +public: + /** + * Creates a new XdgOutputManager. + * Note: after constructing the XdgOutputManager it is not yet valid and one needs + * to call setup. In order to get a ready to use XdgOutputManager prefer using + * Registry::createXdgOutputManager. + **/ + explicit XdgOutputManager(QObject *parent = nullptr); + virtual ~XdgOutputManager(); + + /** + * Setup this XdgOutputManager to manage the @p xdgoutputmanager. + * When using Registry::createXdgOutputManager there is no need to call this + * method. + **/ + void setup(zxdg_output_manager_v1 *xdgoutputmanager); + /** + * @returns @c true if managing a zxdg_output_manager_v1. + **/ + bool isValid() const; + /** + * Releases the zxdg_output_manager_v1 interface. + * After the interface has been released the XdgOutputManager instance is no + * longer valid and can be setup with another zxdg_output_manager_v1 interface. + **/ + void release(); + /** + * Destroys the data held by this XdgOutputManager. + * 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 zxdg_output_manager_v1 interface + * once there is a new connection available. + * + * It is suggested to connect this method to ConnectionThread::connectionDied: + * @code + * connect(connection, &ConnectionThread::connectionDied, xdgoutputmanager, &XdgOutputManager::destroy); + * @endcode + * + * @see release + **/ + void destroy(); + + /** + * Sets the @p queue to use for creating objects with this XdgOutputManager. + **/ + void setEventQueue(EventQueue *queue); + /** + * @returns The event queue to use for creating objects with this XdgOutputManager. + **/ + EventQueue *eventQueue(); + + XdgOutput *getXdgOutput(Output *output, QObject *parent = nullptr); + + operator zxdg_output_manager_v1*(); + operator zxdg_output_manager_v1*() const; + +Q_SIGNALS: + /** + * The corresponding global for this interface on the Registry got removed. + * + * This signal gets only emitted if the XdgOutputManager got created by + * Registry::createXdgOutputManager + **/ + void removed(); + +private: + class Private; + QScopedPointer d; +}; + +/** + * @short Wrapper for the zxdg_output_v1 interface. + * + * This class provides a convenient wrapper for the zxdg_output_v1 interface. + * + * The XdgOutputManager can be used as a drop-in replacement for any zxdg_output_v1 + * pointer as it provides matching cast operators. + * + * This protocol provides a potentially more correct size and position of the screen + * than Output with respect to scaling. + * + * @see Registry + **/ + +class KWAYLANDCLIENT_EXPORT XdgOutput : public QObject +{ + Q_OBJECT +public: + virtual ~XdgOutput(); + + /** + * Setup this XdgOutput to manage the @p xdgoutput. + * When using XdgOutputManager::createXdgOutput there is no need to call this + * method. + **/ + void setup(zxdg_output_v1 *xdgoutput); + /** + * @returns @c true if managing a zxdg_output_v1. + **/ + bool isValid() const; + /** + * Releases the zxdg_output_v1 interface. + * After the interface has been released the XdgOutput instance is no + * longer valid and can be setup with another zxdg_output_v1 interface. + **/ + void release(); + /** + * Destroys the data held by this XdgOutput. + * 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 zxdg_output_v1 interface + * once there is a new connection available. + * + * It is suggested to connect this method to ConnectionThread::connectionDied: + * @code + * connect(connection, &ConnectionThread::connectionDied, xdgoutput, &XdgOutput::destroy); + * @endcode + * + * @see release + **/ + void destroy(); + + operator zxdg_output_v1*(); + operator zxdg_output_v1*() const; + + /** + * The top left position of the output in compositor co-ordinates + */ + QPoint logicalPosition() const; + + /** + * The size of the output in compositor co-ordinates + * (i.e pixel size / output scale) + */ + QSize logicalSize() const; + +Q_SIGNALS: + /** + * Emitted when the logical position or size changes + */ + void changed(); + +private: + friend class XdgOutputManager; + explicit XdgOutput(QObject *parent = nullptr); + class Private; + QScopedPointer d; +}; + + +} +} + +#endif diff --git a/src/client/xdgoutput.cpp b/src/client/xdgoutput.cpp new file mode 100644 --- /dev/null +++ b/src/client/xdgoutput.cpp @@ -0,0 +1,240 @@ +/**************************************************************************** +Copyright 2018 David Edmundson + +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 "xdgoutput.h" +#include "event_queue.h" +#include "wayland_pointer_p.h" +#include "output.h" + +#include +#include + +#include + +namespace KWayland +{ +namespace Client +{ + +class XdgOutputManager::Private +{ +public: + Private() = default; + + void setup(zxdg_output_manager_v1 *arg); + + WaylandPointer xdgoutputmanager; + EventQueue *queue = nullptr; +}; + +XdgOutputManager::XdgOutputManager(QObject *parent) + : QObject(parent) + , d(new Private) +{ +} + +void XdgOutputManager::Private::setup(zxdg_output_manager_v1 *arg) +{ + Q_ASSERT(arg); + Q_ASSERT(!xdgoutputmanager); + xdgoutputmanager.setup(arg); +} + +XdgOutputManager::~XdgOutputManager() +{ + release(); +} + +void XdgOutputManager::setup(zxdg_output_manager_v1 *xdgoutputmanager) +{ + d->setup(xdgoutputmanager); +} + +void XdgOutputManager::release() +{ + d->xdgoutputmanager.release(); +} + +void XdgOutputManager::destroy() +{ + d->xdgoutputmanager.destroy(); +} + +XdgOutputManager::operator zxdg_output_manager_v1*() { + return d->xdgoutputmanager; +} + +XdgOutputManager::operator zxdg_output_manager_v1*() const { + return d->xdgoutputmanager; +} + +bool XdgOutputManager::isValid() const +{ + return d->xdgoutputmanager.isValid(); +} + +void XdgOutputManager::setEventQueue(EventQueue *queue) +{ + d->queue = queue; +} + +EventQueue *XdgOutputManager::eventQueue() +{ + return d->queue; +} + +XdgOutput *XdgOutputManager::getXdgOutput(Output *output, QObject *parent) +{ + Q_ASSERT(isValid()); + auto p = new XdgOutput(parent); + auto w = zxdg_output_manager_v1_get_xdg_output(d->xdgoutputmanager, *output); + if (d->queue) { + d->queue->addProxy(w); + } + p->setup(w); + return p; +} + +struct XdgOutputBuffer +{ + QPoint logicalPosition; + QSize logicalSize; +}; + +class XdgOutput::Private +{ +public: + Private(XdgOutput *q); + + void setup(zxdg_output_v1 *arg); + + WaylandPointer xdgoutput; + + XdgOutputBuffer current; + XdgOutputBuffer pending; + +private: + XdgOutput *q; + +private: + static void logical_positionCallback(void *data, zxdg_output_v1 *zxdg_output_v1, int32_t x, int32_t y); + static void logical_sizeCallback(void *data, zxdg_output_v1 *zxdg_output_v1, int32_t width, int32_t height); + static void doneCallback(void *data, zxdg_output_v1 *zxdg_output_v1); + + static const zxdg_output_v1_listener s_listener; +}; + +const zxdg_output_v1_listener XdgOutput::Private::s_listener = { + logical_positionCallback, + logical_sizeCallback, + doneCallback +}; + +void XdgOutput::Private::logical_positionCallback(void *data, zxdg_output_v1 *zxdg_output_v1, int32_t x, int32_t y) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->xdgoutput == zxdg_output_v1); + p->pending.logicalPosition = QPoint(x,y); +} + +void XdgOutput::Private::logical_sizeCallback(void *data, zxdg_output_v1 *zxdg_output_v1, int32_t width, int32_t height) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->xdgoutput == zxdg_output_v1); + p->pending.logicalSize = QSize(width,height); +} + +void XdgOutput::Private::doneCallback(void *data, zxdg_output_v1 *zxdg_output_v1) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->xdgoutput == zxdg_output_v1); + std::swap(p->current, p->pending); + + if (p->current.logicalSize != p->pending.logicalSize || + p->current.logicalPosition != p->pending.logicalPosition) { + emit p->q->changed(); + } +} + +XdgOutput::Private::Private(XdgOutput *q) + : q(q) +{ +} + +XdgOutput::XdgOutput(QObject *parent) + : QObject(parent) + , d(new Private(this)) +{ +} + +void XdgOutput::Private::setup(zxdg_output_v1 *arg) +{ + Q_ASSERT(arg); + Q_ASSERT(!xdgoutput); + xdgoutput.setup(arg); + zxdg_output_v1_add_listener(xdgoutput, &s_listener, this); +} + +XdgOutput::~XdgOutput() +{ + release(); +} + +void XdgOutput::setup(zxdg_output_v1 *xdgoutput) +{ + d->setup(xdgoutput); +} + +void XdgOutput::release() +{ + d->xdgoutput.release(); +} + +void XdgOutput::destroy() +{ + d->xdgoutput.destroy(); +} + +QSize XdgOutput::logicalSize() const +{ + return d->current.logicalSize; +} + +QPoint XdgOutput::logicalPosition() const +{ + return d->current.logicalPosition; +} + +XdgOutput::operator zxdg_output_v1*() { + return d->xdgoutput; +} + +XdgOutput::operator zxdg_output_v1*() const { + return d->xdgoutput; +} + +bool XdgOutput::isValid() const +{ + return d->xdgoutput.isValid(); +} + + +} +} + diff --git a/src/client/xdgshell_v6.cpp b/src/client/xdgshell_v6.cpp --- a/src/client/xdgshell_v6.cpp +++ b/src/client/xdgshell_v6.cpp @@ -160,24 +160,24 @@ } uint32_t gravity = 0; - if (positioner.anchorEdge().testFlag(Qt::LeftEdge)) { + if (positioner.gravity().testFlag(Qt::LeftEdge)) { gravity |= ZXDG_POSITIONER_V6_GRAVITY_LEFT; } - if (positioner.anchorEdge().testFlag(Qt::TopEdge)) { + if (positioner.gravity().testFlag(Qt::TopEdge)) { gravity |= ZXDG_POSITIONER_V6_GRAVITY_TOP; } - if (positioner.anchorEdge().testFlag(Qt::RightEdge)) { + if (positioner.gravity().testFlag(Qt::RightEdge)) { gravity |= ZXDG_POSITIONER_V6_GRAVITY_RIGHT; } - if (positioner.anchorEdge().testFlag(Qt::BottomEdge)) { + if (positioner.gravity().testFlag(Qt::BottomEdge)) { gravity |= ZXDG_POSITIONER_V6_GRAVITY_BOTTOM; } - zxdg_positioner_v6_set_anchor(p, anchor); if (gravity != 0) { zxdg_positioner_v6_set_gravity(p, gravity); } uint32_t constraint = 0; + if (positioner.constraints().testFlag(XdgPositioner::Constraint::SlideX)) { constraint |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_X; } @@ -191,7 +191,7 @@ constraint |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_Y; } if (positioner.constraints().testFlag(XdgPositioner::Constraint::ResizeX)) { - constraint |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y; + constraint |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_X; } if (positioner.constraints().testFlag(XdgPositioner::Constraint::ResizeY)) { constraint |= ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y; 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_unstable_v1.cpp plasmawindowmanagement_interface.cpp pointerconstraints_interface.cpp pointerconstraints_interface_v1.cpp @@ -54,6 +55,7 @@ xdgforeign_v2_interface.cpp xdgforeign_interface.cpp xdgshell_v6_interface.cpp + xdgoutput_interface.cpp ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS @@ -72,6 +74,11 @@ ) ecm_add_wayland_server_protocol(SERVER_LIB_SRCS + PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/plasma-virtual-desktop-unstable-v1.xml + BASENAME plasma-virtual-desktop-unstable-v1 +) + +ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/plasma-window-management.xml BASENAME plasma-window-management ) @@ -180,13 +187,21 @@ BASENAME remote-access ) +ecm_add_wayland_server_protocol(SERVER_LIB_SRCS + PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/xdg-output-unstable-v1.xml + BASENAME xdg-output +) + set(SERVER_GENERATED_SRCS ${CMAKE_CURRENT_BINARY_DIR}/wayland-output-management-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-output-management-server-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-org_kde_kwin_outputdevice-client-protocol.h ${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-unstable-v1-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-virtual-desktop-unstable-v1-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 @@ -227,6 +242,8 @@ ${CMAKE_CURRENT_BINARY_DIR}/wayland-xdg-foreign-unstable-v2-server-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-idle-inhibit-unstable-v1-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-idle-inhibit-unstable-v1-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-output-unstable-v1-client-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-output-unstable-v1-server-protocol.h ) set_source_files_properties(${SERVER_GENERATED_SRCS} PROPERTIES SKIP_AUTOMOC ON) @@ -289,6 +306,7 @@ pointerconstraints_interface.h pointergestures_interface.h plasmashell_interface.h + plasmavirtualdesktop_interface_unstable_v1.h plasmawindowmanagement_interface.h qtsurfaceextension_interface.h region_interface.h @@ -306,6 +324,7 @@ touch_interface.h xdgshell_interface.h xdgforeign_interface.h + xdgoutput_interface.h ) install(FILES diff --git a/src/server/datadevice_interface.cpp b/src/server/datadevice_interface.cpp --- a/src/server/datadevice_interface.cpp +++ b/src/server/datadevice_interface.cpp @@ -130,6 +130,9 @@ wl_resource_post_error(dataSource->resource(), WL_DATA_SOURCE_ERROR_INVALID_SOURCE, "Data source is for drag and drop"); return; } + if (selection == dataSource) { + return; + } Q_Q(DataDeviceInterface); QObject::disconnect(selectionUnboundConnection); QObject::disconnect(selectionDestroyedConnection); 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,8 @@ class XdgForeignInterface; class AppMenuManagerInterface; class ServerSideDecorationPaletteManagerInterface; +class PlasmaVirtualDesktopManagementV1Interface; +class XdgOutputManagerInterface; /** * @brief Class holding the Wayland server display loop. @@ -258,6 +260,22 @@ **/ ServerSideDecorationPaletteManagerInterface *createServerSideDecorationPaletteManager(QObject *parent = nullptr); + /** + * Creates the XdgOutputManagerInterface + * + * @return the created manager + * @since 5.47 + */ + XdgOutputManagerInterface *createXdgOutputManager(QObject *parent = nullptr); + + + /** + * Creates the PlasmaVirtualDesktopManagementInterfaceUnstableV1 in interface @p version. + * + * @returns The created manager object + * @since 5.46 + **/ + PlasmaVirtualDesktopManagementV1Interface *createPlasmaVirtualDesktopManagement(QObject *parent = nullptr); /** * Gets the ClientConnection for the given @p client. 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,8 @@ #include "xdgshell_v6_interface_p.h" #include "appmenu_interface.h" #include "server_decoration_palette_interface.h" +#include "plasmavirtualdesktop_interface_unstable_v1.h" +#include "xdgoutput_interface.h" #include #include @@ -457,6 +459,21 @@ return b; } + +PlasmaVirtualDesktopManagementV1Interface *Display::createPlasmaVirtualDesktopManagement(QObject *parent) +{ + auto b = new PlasmaVirtualDesktopManagementV1Interface(this, parent); + connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; }); + return b; +} + +XdgOutputManagerInterface *Display::createXdgOutputManager(QObject *parent) +{ + auto b = new XdgOutputManagerInterface(this, parent); + connect(this, &Display::aboutToTerminate, b, [this, b] { delete b; }); + return b; +} + void Display::createShm() { Q_ASSERT(d->display); diff --git a/src/server/plasmashell_interface.h b/src/server/plasmashell_interface.h --- a/src/server/plasmashell_interface.h +++ b/src/server/plasmashell_interface.h @@ -135,10 +135,17 @@ * @returns true if this window doesn't want to be listed * in the taskbar * @since 5.5 - */ + **/ bool skipTaskbar() const; /** + * @returns true if this window doesn't want to be listed + * in a window switcher + * @since 5.47 + **/ + bool skipSwitcher() const; + + /** * Informs the PlasmaShellSurfaceInterface that the auto-hiding panel got hidden. * Once it is shown again the method {@link showAutoHidingPanel} should be used. * @@ -192,6 +199,10 @@ * A change in the skip taskbar property has been requested */ void skipTaskbarChanged(); + /** + * A change in the skip switcher property has been requested + **/ + void skipSwitcherChanged(); /** * A surface with Role Panel and PanelBehavior AutoHide requested to be hidden. diff --git a/src/server/plasmashell_interface.cpp b/src/server/plasmashell_interface.cpp --- a/src/server/plasmashell_interface.cpp +++ b/src/server/plasmashell_interface.cpp @@ -50,7 +50,7 @@ static const quint32 s_version; }; -const quint32 PlasmaShellInterface::Private::s_version = 4; +const quint32 PlasmaShellInterface::Private::s_version = 5; PlasmaShellInterface::Private::Private(PlasmaShellInterface *q, Display *d) : Global::Private(d, &org_kde_plasma_shell_interface, s_version) @@ -76,6 +76,7 @@ bool m_positionSet = false; PanelBehavior m_panelBehavior = PanelBehavior::AlwaysVisible; bool m_skipTaskbar = false; + bool m_skipSwitcher = false; bool panelTakesFocus = false; private: @@ -85,6 +86,7 @@ static void setRoleCallback(wl_client *client, wl_resource *resource, uint32_t role); static void setPanelBehaviorCallback(wl_client *client, wl_resource *resource, uint32_t flag); static void setSkipTaskbarCallback(wl_client *client, wl_resource *resource, uint32_t skip); + static void setSkipSwitcherCallback(wl_client *client, wl_resource *resource, uint32_t skip); static void panelAutoHideHideCallback(wl_client *client, wl_resource *resource); static void panelAutoHideShowCallback(wl_client *client, wl_resource *resource); static void panelTakesFocusCallback(wl_client *client, wl_resource *resource, uint32_t takesFocus); @@ -165,7 +167,8 @@ setSkipTaskbarCallback, panelAutoHideHideCallback, panelAutoHideShowCallback, - panelTakesFocusCallback + panelTakesFocusCallback, + setSkipSwitcherCallback }; #endif @@ -277,6 +280,14 @@ emit s->q_func()->skipTaskbarChanged(); } +void PlasmaShellSurfaceInterface::Private::setSkipSwitcherCallback(wl_client *client, wl_resource *resource, uint32_t skip) +{ + auto s = cast(resource); + Q_ASSERT(client == *s->client); + s->m_skipSwitcher = (bool)skip; + emit s->q_func()->skipSwitcherChanged(); +} + void PlasmaShellSurfaceInterface::Private::panelAutoHideHideCallback(wl_client *client, wl_resource *resource) { auto s = cast(resource); @@ -361,6 +372,12 @@ return d->m_skipTaskbar; } +bool PlasmaShellSurfaceInterface::skipSwitcher() const +{ + Q_D(); + return d->m_skipSwitcher; +} + void PlasmaShellSurfaceInterface::hideAutoHidingPanel() { Q_D(); diff --git a/src/server/plasmavirtualdesktop_interface_unstable_v1.h b/src/server/plasmavirtualdesktop_interface_unstable_v1.h new file mode 100644 --- /dev/null +++ b/src/server/plasmavirtualdesktop_interface_unstable_v1.h @@ -0,0 +1,171 @@ +/**************************************************************************** +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_UNSTABLE_V1_H +#define KWAYLAND_SERVER_PLASMAVIRTUALDESKTOP_UNSTABLE_V1_H + +#include "global.h" +#include "resource.h" + +#include + +namespace KWayland +{ +namespace Server +{ + +class Display; +class PlasmaVirtualDesktopV1Interface; + +/** + * @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 PlasmaVirtualDesktopManagementV1Interface : public Global +{ + Q_OBJECT +public: + virtual ~PlasmaVirtualDesktopManagementV1Interface(); + + /** + * 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 + */ + PlasmaVirtualDesktopV1Interface *desktop(const QString &id); + + /** + * @returns A desktop identified uniquely by this id, if not found + * a new desktop will be created for this id at a given position. + * @param id the id for the desktop + * @param position the position the desktop will be in, if not provided, + * it will be appended at the end. If the desktop was already + * existing, position is ignored. + */ + PlasmaVirtualDesktopV1Interface *createDesktop(const QString &id, quint32 position = std::numeric_limits::max()); + + /** + * 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(); + +Q_SIGNALS: + /** + * A desktop has been activated + */ + void desktopActivated(const QString &id); + + /** + * The client asked to remove a desktop, It's responsibility of the server + * deciding whether to remove it or not. + */ + void desktopRemoveRequested(const QString &id); + + /** + * The client asked to create a desktop, It's responsibility of the server + * deciding whether to create it or not. + * @param name The desired user readable name + * @param position The desired position. Normalized, guaranteed to be in the range 0-count + */ + void desktopCreateRequested(const QString &name, quint32 position); + +private: + explicit PlasmaVirtualDesktopManagementV1Interface(Display *display, QObject *parent = nullptr); + friend class Display; + class Private; + Private *d_func() const; +}; + +class KWAYLANDSERVER_EXPORT PlasmaVirtualDesktopV1Interface : public QObject +{ + Q_OBJECT +public: + virtual ~PlasmaVirtualDesktopV1Interface(); + + /** + * @returns the unique id for this desktop. + * ids are set at creation time by PlasmaVirtualDesktopManagementInterfaceUnstableV1::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; + + /** + * Set this desktop as active or not. + * It's the compositor responsibility to manage the mutual exclusivity of active desktops. + */ + void setActive(bool active); + + /** + * @returns true if this desktop is active. Only one at a time will be. + */ + bool isActive() 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 PlasmaVirtualDesktopV1Interface(PlasmaVirtualDesktopManagementV1Interface *parent); + friend class PlasmaVirtualDesktopManagementV1Interface; + + class Private; + const QScopedPointer d; +}; + +} +} + +#endif diff --git a/src/server/plasmavirtualdesktop_interface_unstable_v1.cpp b/src/server/plasmavirtualdesktop_interface_unstable_v1.cpp new file mode 100644 --- /dev/null +++ b/src/server/plasmavirtualdesktop_interface_unstable_v1.cpp @@ -0,0 +1,402 @@ +/**************************************************************************** +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_unstable_v1.h" +#include "display.h" +#include "global_p.h" +#include "resource_p.h" + +#include +#include + +#include +#include + +namespace KWayland +{ +namespace Server +{ + +class Q_DECL_HIDDEN PlasmaVirtualDesktopV1Interface::Private +{ +public: + Private(PlasmaVirtualDesktopV1Interface *q, PlasmaVirtualDesktopManagementV1Interface *c); + ~Private(); + void createResource(wl_resource *parent, quint32 serial); + + PlasmaVirtualDesktopV1Interface *q; + PlasmaVirtualDesktopManagementV1Interface *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_v1_interface s_interface; +}; + + +class Q_DECL_HIDDEN PlasmaVirtualDesktopManagementV1Interface::Private : public Global::Private +{ +public: + Private(PlasmaVirtualDesktopManagementV1Interface *q, Display *d); + + QVector resources; + QList desktops; + quint32 rows = 0; + quint32 columns = 0; + + inline QList::const_iterator constFindDesktop(const QString &id); + inline QList::iterator findDesktop(const QString &id); +private: + void bind(wl_client *client, uint32_t version, uint32_t id) override; + + static void unbind(wl_resource *resource); + static Private *cast(wl_resource *r) { + return reinterpret_cast(wl_resource_get_user_data(r)); + } + + static void getVirtualDesktopCallback(wl_client *client, wl_resource *resource, uint32_t serial, const char *id); + static void requestCreateVirtualDesktopCallback(wl_client *client, wl_resource *resource, const char *name, uint32_t position); + static void requestRemoveVirtualDesktopCallback(wl_client *client, wl_resource *resource, const char *id); + + PlasmaVirtualDesktopManagementV1Interface *q; + + static const struct org_kde_plasma_virtual_desktop_management_v1_interface s_interface; + static const quint32 s_version; +}; + +const quint32 PlasmaVirtualDesktopManagementV1Interface::Private::s_version = 1; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct org_kde_plasma_virtual_desktop_management_v1_interface PlasmaVirtualDesktopManagementV1Interface::Private::s_interface = { + getVirtualDesktopCallback, + requestCreateVirtualDesktopCallback, + requestRemoveVirtualDesktopCallback +}; +#endif + +inline QList::const_iterator PlasmaVirtualDesktopManagementV1Interface::Private::constFindDesktop(const QString &id) +{ + return std::find_if( desktops.constBegin(), + desktops.constEnd(), + [id]( const PlasmaVirtualDesktopV1Interface *desk ){ return desk->id() == id; } ); +} + +inline QList::iterator PlasmaVirtualDesktopManagementV1Interface::Private::findDesktop(const QString &id) +{ + return std::find_if( desktops.begin(), + desktops.end(), + [id]( const PlasmaVirtualDesktopV1Interface *desk ){ return desk->id() == id; } ); +} + +void PlasmaVirtualDesktopManagementV1Interface::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 PlasmaVirtualDesktopManagementV1Interface::Private::requestCreateVirtualDesktopCallback(wl_client *client, wl_resource *resource, const char *name, uint32_t position) +{ + Q_UNUSED(client) + auto s = cast(resource); + emit s->q->desktopCreateRequested(QString::fromUtf8(name), qBound(0, position, (quint32)s->desktops.count())); +} + +void PlasmaVirtualDesktopManagementV1Interface::Private::requestRemoveVirtualDesktopCallback(wl_client *client, wl_resource *resource, const char *id) +{ + Q_UNUSED(client) + auto s = cast(resource); + emit s->q->desktopRemoveRequested(QString::fromUtf8(id)); +} + +PlasmaVirtualDesktopManagementV1Interface::Private::Private(PlasmaVirtualDesktopManagementV1Interface *q, Display *d) + : Global::Private(d, &org_kde_plasma_virtual_desktop_management_v1_interface, s_version) + , q(q) +{ +} + +void PlasmaVirtualDesktopManagementV1Interface::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_v1_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_v1_send_desktop_created(resource, (*it)->id().toUtf8().constData(), i++); + } + org_kde_plasma_virtual_desktop_management_v1_send_done(resource); +} + +void PlasmaVirtualDesktopManagementV1Interface::Private::unbind(wl_resource *resource) +{ + auto dm = reinterpret_cast(wl_resource_get_user_data(resource)); + dm->resources.removeAll(resource); +} + +PlasmaVirtualDesktopManagementV1Interface::PlasmaVirtualDesktopManagementV1Interface(Display *display, QObject *parent) + : Global(new Private(this, display), parent) +{ +} + +PlasmaVirtualDesktopManagementV1Interface::~PlasmaVirtualDesktopManagementV1Interface() +{} + +PlasmaVirtualDesktopManagementV1Interface::Private *PlasmaVirtualDesktopManagementV1Interface::d_func() const +{ + return reinterpret_cast(d.data()); +} + +PlasmaVirtualDesktopV1Interface *PlasmaVirtualDesktopManagementV1Interface::desktop(const QString &id) +{ + Q_D(); + auto i = d->constFindDesktop(id); + if (i != d->desktops.constEnd()) { + return *i; + } + return nullptr; +} + +PlasmaVirtualDesktopV1Interface *PlasmaVirtualDesktopManagementV1Interface::createDesktop(const QString &id, quint32 position) +{ + Q_D(); + auto i = d->constFindDesktop(id); + if (i != d->desktops.constEnd()) { + return *i; + } + + const quint32 actualPosition = qMin(position, (quint32)d->desktops.count()); + + PlasmaVirtualDesktopV1Interface *desktop = new PlasmaVirtualDesktopV1Interface(this); + desktop->d->id = id; + for (auto it = desktop->d->resources.constBegin(); it != desktop->d->resources.constEnd(); ++it) { + org_kde_plasma_virtual_desktop_v1_send_desktop_id(*it, id.toUtf8().constData()); + } + + //activate the first desktop TODO: to be done here? + if (d->desktops.isEmpty()) { + desktop->d->active = true; + } + + d->desktops.insert(actualPosition, desktop); + //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_v1_send_desktop_removed(*it, id.toUtf8().constData()); + } + + d->desktops.erase(i); + } + } + ); + + for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { + org_kde_plasma_virtual_desktop_management_v1_send_desktop_created(*it, id.toUtf8().constData(), actualPosition); + } + + return desktop; +} + +void PlasmaVirtualDesktopManagementV1Interface::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_v1_send_removed(*it); + } + + for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { + org_kde_plasma_virtual_desktop_management_v1_send_desktop_removed(*it, id.toUtf8().constData()); + } + + d->desktops.erase(deskIt); + (*deskIt)->deleteLater(); +} + +QList PlasmaVirtualDesktopManagementV1Interface::desktops() const +{ + Q_D(); + return d->desktops; +} + +void PlasmaVirtualDesktopManagementV1Interface::sendDone() +{ + Q_D(); + for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { + org_kde_plasma_virtual_desktop_management_v1_send_done(*it); + } +} + +//// PlasmaVirtualDesktopInterfaceUnstableV1 + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct org_kde_plasma_virtual_desktop_v1_interface PlasmaVirtualDesktopV1Interface::Private::s_interface = { + requestActivateCallback +}; +#endif + +void PlasmaVirtualDesktopV1Interface::Private::requestActivateCallback(wl_client *client, wl_resource *resource) +{ + Q_UNUSED(client) + auto s = cast(resource); + emit s->q->activateRequested(); +} + +PlasmaVirtualDesktopV1Interface::Private::Private(PlasmaVirtualDesktopV1Interface *q, PlasmaVirtualDesktopManagementV1Interface *c) + : q(q), + vdm(c) +{ +} + +PlasmaVirtualDesktopV1Interface::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_v1_send_removed(r); + wl_resource_destroy(r); + wl_client_flush(client); + } +} + +void PlasmaVirtualDesktopV1Interface::Private::unbind(wl_resource *resource) +{ + Private *p = reinterpret_cast(wl_resource_get_user_data(resource)); + p->resources.removeAll(resource); +} + +void PlasmaVirtualDesktopV1Interface::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_v1_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_v1_send_desktop_id(resource, id.toUtf8().constData()); + if (!name.isEmpty()) { + org_kde_plasma_virtual_desktop_v1_send_name(resource, name.toUtf8().constData()); + } + + if (active) { + org_kde_plasma_virtual_desktop_v1_send_activated(resource); + } + + c->flush(); +} + +PlasmaVirtualDesktopV1Interface::PlasmaVirtualDesktopV1Interface(PlasmaVirtualDesktopManagementV1Interface *parent) + : QObject(parent), + d(new Private(this, parent)) +{ +} + +PlasmaVirtualDesktopV1Interface::~PlasmaVirtualDesktopV1Interface() +{} + +QString PlasmaVirtualDesktopV1Interface::id() const +{ + return d->id; +} + +void PlasmaVirtualDesktopV1Interface::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_v1_send_name(*it, name.toUtf8().constData()); + } +} + +QString PlasmaVirtualDesktopV1Interface::name() const +{ + return d->name; +} + +void PlasmaVirtualDesktopV1Interface::setActive(bool active) +{ + if (d->active == active) { + return; + } + + d->active = active; + if (active) { + for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { + org_kde_plasma_virtual_desktop_v1_send_activated(*it); + } + } else { + for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { + org_kde_plasma_virtual_desktop_v1_send_deactivated(*it); + } + } +} + +bool PlasmaVirtualDesktopV1Interface::isActive() const +{ + return d->active; +} + +void PlasmaVirtualDesktopV1Interface::sendDone() +{ + for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { + org_kde_plasma_virtual_desktop_v1_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 PlasmaVirtualDesktopManagementV1Interface; /** * @todo Add documentation @@ -72,6 +73,20 @@ **/ void unmapWindow(PlasmaWindowInterface *window); + /** + * Associate a PlasmaVirtualDesktopManagementInterfaceUnstableV1 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.48 + */ + void setPlasmaVirtualDesktopManagementInterfaceUnstableV1(PlasmaVirtualDesktopManagementV1Interface *manager); + + /** + * @returns the PlasmaVirtualDesktopManagementInterfaceUnstableV1 associated to this PlasmaWindowManagementInterface + * @since 5.48 + */ + PlasmaVirtualDesktopManagementV1Interface *plasmaVirtualDesktopManagementInterfaceUnstableV1() const; + Q_SIGNALS: void requestChangeShowingDesktop(ShowingDesktopState requestedState); @@ -94,7 +109,12 @@ void setTitle(const QString &title); void setAppId(const QString &appId); void setPid(quint32 pid); - void setVirtualDesktop(quint32 desktop); +#ifndef KWAYLANDSERVER_NO_DEPRECATED + /** + * @deprecated use addPlasmaVirtualDesktop and removePlasmaVirtualDesktop + */ + void KWAYLANDSERVER_DEPRECATED setVirtualDesktop(quint32 desktop); +#endif void setActive(bool set); void setMinimized(bool set); void setMaximized(bool set); @@ -108,6 +128,7 @@ void setMaximizeable(bool set); void setFullscreenable(bool set); void setSkipTaskbar(bool skip); + void setSkipSwitcher(bool skip); /** * @deprecated since 5.28 use setIcon * @see setIcon @@ -132,6 +153,7 @@ */ void setResizable(bool set); /** + * FIXME: still relevant with new desktops? * @since 5.22 */ void setVirtualDesktopChangeable(bool set); @@ -181,6 +203,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.48 + */ + void addPlasmaVirtualDesktop(const QString &id); + + /** + * Removes a visrtual desktop from a window + * + * @since 5.48 + */ + 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.48 + */ + QStringList plasmaVirtualDesktops() const; + Q_SIGNALS: void closeRequested(); /** @@ -191,7 +237,12 @@ * @since 5.22 */ void resizeRequested(); - void virtualDesktopRequested(quint32 desktop); +#ifndef KWAYLANDSERVER_NO_DEPRECATED + /** + * @deprecated use enterPlasmaVirtualDesktopRequested and leavePlasmaVirtualDesktopRequested instead + */ + void KWAYLANDSERVER_DEPRECATED virtualDesktopRequested(quint32 desktop); +#endif void activeRequested(bool set); void minimizedRequested(bool set); void maximizedRequested(bool set); @@ -204,6 +255,7 @@ void maximizeableRequested(bool set); void fullscreenableRequested(bool set); void skipTaskbarRequested(bool set); + void skipSwitcherRequested(bool set); QRect minimizedGeometriesChanged(); /** * @since 5.22 @@ -222,10 +274,33 @@ */ void resizableRequested(bool set); /** + * FIXME: still relevant with new virtual desktops? * @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.48 + */ + void enterPlasmaVirtualDesktopRequested(const QString &desktop); + + /** + * Emitted when the client wishes this window to enter in + * a new virtual desktop to be created for it. + * The server will decide whether to consent this request + * @since 5.48 + */ + void enterNewPlasmaVirtualDesktopRequested(); + + /** + * Emitted when the client wishes to remove this window from a virtual desktop. + * The server will decide whether to consent this request + * @since 5.48 + */ + 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_unstable_v1.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,9 @@ 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)); } @@ -119,7 +125,7 @@ static const struct org_kde_plasma_window_interface s_interface; }; -const quint32 PlasmaWindowManagementInterface::Private::s_version = 7; +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) @@ -255,6 +261,12 @@ return window; } +QList PlasmaWindowManagementInterface::windows() const +{ + Q_D(); + return d->windows; +} + void PlasmaWindowManagementInterface::unmapWindow(PlasmaWindowInterface *window) { if (!window) { @@ -266,6 +278,21 @@ window->d->unmap(); } +void PlasmaWindowManagementInterface::setPlasmaVirtualDesktopManagementInterfaceUnstableV1(PlasmaVirtualDesktopManagementV1Interface *manager) +{ + Q_D(); + if (d->plasmaVirtualDesktopManagementInterface == manager) { + return; + } + d->plasmaVirtualDesktopManagementInterface = manager; +} + +PlasmaVirtualDesktopManagementV1Interface *PlasmaWindowManagementInterface::plasmaVirtualDesktopManagementInterfaceUnstableV1() 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 +303,10 @@ requestMoveCallback, requestResizeCallback, destroyCallback, - getIconCallback + getIconCallback, + requestEnterVirtualDesktopCallback, + requestEnterNewVirtualDesktopCallback, + requestLeaveVirtualDesktopCallback }; #endif @@ -328,6 +358,9 @@ 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()); } @@ -425,6 +458,27 @@ ); } +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) { @@ -602,6 +656,9 @@ 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); } @@ -681,10 +738,12 @@ d->setTitle(title); } +#ifndef KWAYLANDSERVER_NO_DEPRECATED void PlasmaWindowInterface::setVirtualDesktop(quint32 desktop) { d->setVirtualDesktop(desktop); } +#endif void PlasmaWindowInterface::unmap() { @@ -728,7 +787,39 @@ void PlasmaWindowInterface::setOnAllDesktops(bool set) { + //the deprecated vd management d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ON_ALL_DESKTOPS, set); + + if (!d->wm->plasmaVirtualDesktopManagementInterfaceUnstableV1()) { + 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->plasmaVirtualDesktopManagementInterfaceUnstableV1()->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) @@ -761,6 +852,11 @@ 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) { @@ -773,6 +869,58 @@ d->setIcon(icon); } +void PlasmaWindowInterface::addPlasmaVirtualDesktop(const QString &id) +{ + //don't add a desktop we're not sure it exists + if (!d->wm->plasmaVirtualDesktopManagementInterfaceUnstableV1() || d->plasmaVirtualDesktops.contains(id)) { + return; + } + + PlasmaVirtualDesktopV1Interface *desktop = d->wm->plasmaVirtualDesktopManagementInterfaceUnstableV1()->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->plasmaVirtualDesktopManagementInterfaceUnstableV1()->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); diff --git a/src/server/surface_interface.cpp b/src/server/surface_interface.cpp --- a/src/server/surface_interface.cpp +++ b/src/server/surface_interface.cpp @@ -181,93 +181,89 @@ Q_ASSERT(lockedPointer.isNull()); Q_ASSERT(confinedPointer.isNull()); lockedPointer = QPointer(lock); + + auto cleanUp = [this]() { + lockedPointer.clear(); + disconnect(constrainsOneShotConnection); + constrainsOneShotConnection = QMetaObject::Connection(); + disconnect(constrainsUnboundConnection); + constrainsUnboundConnection = QMetaObject::Connection(); + emit q_func()->pointerConstraintsChanged(); + }; + if (lock->lifeTime() == LockedPointerInterface::LifeTime::OneShot) { constrainsOneShotConnection = QObject::connect(lock, &LockedPointerInterface::lockedChanged, q_func(), - [this] { - if (lockedPointer.isNull()) { + [this, cleanUp] { + if (lockedPointer.isNull() || lockedPointer->isLocked()) { return; } - if (!lockedPointer->isLocked()) { - lockedPointer.clear(); - disconnect(constrainsOneShotConnection); - constrainsOneShotConnection = QMetaObject::Connection(); - disconnect(constrainsUnboundConnection); - constrainsUnboundConnection = QMetaObject::Connection(); - emit q_func()->pointerConstraintsChanged(); - } + cleanUp(); } ); } constrainsUnboundConnection = QObject::connect(lock, &LockedPointerInterface::unbound, q_func(), - [this] { + [this, cleanUp] { if (lockedPointer.isNull()) { return; } - lockedPointer.clear(); - disconnect(constrainsOneShotConnection); - constrainsOneShotConnection = QMetaObject::Connection(); - disconnect(constrainsUnboundConnection); - constrainsUnboundConnection = QMetaObject::Connection(); - emit q_func()->pointerConstraintsChanged(); + cleanUp(); } ); emit q_func()->pointerConstraintsChanged(); } -void SurfaceInterface::Private::installIdleInhibitor(IdleInhibitorInterface *inhibitor) -{ - idleInhibitors << inhibitor; - QObject::connect(inhibitor, &IdleInhibitorInterface::aboutToBeUnbound, q, - [this, inhibitor] { - idleInhibitors.removeOne(inhibitor); - if (idleInhibitors.isEmpty()) { - emit q_func()->inhibitsIdleChanged(); - } - } - ); - if (idleInhibitors.count() == 1) { - emit q_func()->inhibitsIdleChanged(); - } -} - void SurfaceInterface::Private::installPointerConstraint(ConfinedPointerInterface *confinement) { Q_ASSERT(lockedPointer.isNull()); Q_ASSERT(confinedPointer.isNull()); confinedPointer = QPointer(confinement); + + auto cleanUp = [this]() { + confinedPointer.clear(); + disconnect(constrainsOneShotConnection); + constrainsOneShotConnection = QMetaObject::Connection(); + disconnect(constrainsUnboundConnection); + constrainsUnboundConnection = QMetaObject::Connection(); + emit q_func()->pointerConstraintsChanged(); + }; + if (confinement->lifeTime() == ConfinedPointerInterface::LifeTime::OneShot) { constrainsOneShotConnection = QObject::connect(confinement, &ConfinedPointerInterface::confinedChanged, q_func(), - [this] { - if (confinedPointer.isNull()) { + [this, cleanUp] { + if (confinedPointer.isNull() || confinedPointer->isConfined()) { return; } - if (!confinedPointer->isConfined()) { - confinedPointer.clear(); - disconnect(constrainsOneShotConnection); - constrainsOneShotConnection = QMetaObject::Connection(); - disconnect(constrainsUnboundConnection); - constrainsUnboundConnection = QMetaObject::Connection(); - emit q_func()->pointerConstraintsChanged(); - } + cleanUp(); } ); } constrainsUnboundConnection = QObject::connect(confinement, &ConfinedPointerInterface::unbound, q_func(), - [this] { + [this, cleanUp] { if (confinedPointer.isNull()) { return; } - confinedPointer.clear(); - disconnect(constrainsOneShotConnection); - constrainsOneShotConnection = QMetaObject::Connection(); - disconnect(constrainsUnboundConnection); - constrainsUnboundConnection = QMetaObject::Connection(); - emit q_func()->pointerConstraintsChanged(); + cleanUp(); } ); emit q_func()->pointerConstraintsChanged(); } +void SurfaceInterface::Private::installIdleInhibitor(IdleInhibitorInterface *inhibitor) +{ + idleInhibitors << inhibitor; + QObject::connect(inhibitor, &IdleInhibitorInterface::aboutToBeUnbound, q, + [this, inhibitor] { + idleInhibitors.removeOne(inhibitor); + if (idleInhibitors.isEmpty()) { + emit q_func()->inhibitsIdleChanged(); + } + } + ); + if (idleInhibitors.count() == 1) { + emit q_func()->inhibitsIdleChanged(); + } +} + #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct wl_surface_interface SurfaceInterface::Private::s_interface = { resourceDestroyedCallback, diff --git a/src/server/xdgforeign_v2_interface.cpp b/src/server/xdgforeign_v2_interface.cpp --- a/src/server/xdgforeign_v2_interface.cpp +++ b/src/server/xdgforeign_v2_interface.cpp @@ -95,6 +95,7 @@ void XdgExporterUnstableV2Interface::Private::destroyCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) + Q_UNUSED(resource) } void XdgExporterUnstableV2Interface::Private::exportCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * surface) @@ -237,6 +238,7 @@ void XdgImporterUnstableV2Interface::Private::destroyCallback(wl_client *client, wl_resource *resource) { Q_UNUSED(client) + Q_UNUSED(resource) } void XdgImporterUnstableV2Interface::Private::importCallback(wl_client *client, wl_resource *resource, uint32_t id, const char *h) @@ -438,6 +440,8 @@ void XdgImportedUnstableV2Interface::Private::setParentOfCallback(wl_client *client, wl_resource *resource, wl_resource * surface) { + Q_UNUSED(client) + auto s = cast(resource); SurfaceInterface *surf = SurfaceInterface::get(surface); diff --git a/src/server/xdgoutput_interface.h b/src/server/xdgoutput_interface.h new file mode 100644 --- /dev/null +++ b/src/server/xdgoutput_interface.h @@ -0,0 +1,121 @@ +/**************************************************************************** +Copyright 2018 David Edmundson + +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_XDGOUTPUT_H +#define KWAYLAND_SERVER_XDGOUTPUT_H + +#include "global.h" +#include "resource.h" + + +#include + + +/* + * In terms of protocol XdgOutputInterface are a resource + * but for the sake of sanity, we should treat XdgOutputs as globals like Output is + * Hence this doesn't match most of kwayland API paradigms. + */ + +namespace KWayland +{ +namespace Server +{ + +class Display; +class OutputInterface; +class XdgOutputInterface; + +/** + * Global manager for XdgOutputs + * @since 5.47 + */ +class KWAYLANDSERVER_EXPORT XdgOutputManagerInterface : public Global +{ + Q_OBJECT +public: + virtual ~XdgOutputManagerInterface(); + /** + * Creates an XdgOutputInterface object for an existing Output + * which exposes XDG specific properties of outputs + * + * @arg output the wl_output interface this XDG output is for + * @parent the parent of the newly created object + */ + XdgOutputInterface* createXdgOutput(OutputInterface *output, QObject *parent); +private: + explicit XdgOutputManagerInterface(Display *display, QObject *parent = nullptr); + friend class Display; + class Private; + Private *d_func() const; +}; + +/** + * Extension to Output + * Users should set all relevant values on creation and on future changes. + * done() should be explicitly called after change batches including initial setting. + * @since 5.47 + */ +class KWAYLANDSERVER_EXPORT XdgOutputInterface : public QObject +{ + Q_OBJECT +public: + virtual ~XdgOutputInterface(); + + /** + * Sets the size of this output in logical co-ordinates. + * Users should call done() after setting all values + */ + void setLogicalSize(const QSize &size); + + /** + * Returns the last set logical size on this output + */ + QSize logicalSize() const; + + /** + * Sets the topleft position of this output in logical co-ordinates. + * Users should call done() after setting all values + * @see OutputInterface::setPosition + */ + void setLogicalPosition(const QPoint &pos); + + /** + * Returns the last set logical position on this output + */ + QPoint logicalPosition() const; + + /** + * Submit changes to all clients + */ + void done(); + +private: + explicit XdgOutputInterface(QObject *parent); + friend class XdgOutputManagerInterface; + + class Private; + QScopedPointer d; +}; + + +} +} + +#endif diff --git a/src/server/xdgoutput_interface.cpp b/src/server/xdgoutput_interface.cpp new file mode 100644 --- /dev/null +++ b/src/server/xdgoutput_interface.cpp @@ -0,0 +1,307 @@ +/**************************************************************************** +Copyright 2018 David Edmundson + +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 "xdgoutput_interface.h" +#include "display.h" +#include "global_p.h" +#include "resource_p.h" +#include "output_interface.h" + +#include + +namespace KWayland +{ +namespace Server +{ + +class XdgOutputManagerInterface::Private : public Global::Private +{ +public: + Private(XdgOutputManagerInterface *q, Display *d); + QHash outputs; + +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 destroyCallback(wl_client *client, wl_resource *resource); + static void getXdgOutputCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * output); + + XdgOutputManagerInterface *q; + static const struct zxdg_output_manager_v1_interface s_interface; + static const quint32 s_version; +}; + +const quint32 XdgOutputManagerInterface::Private::s_version = 1; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct zxdg_output_manager_v1_interface XdgOutputManagerInterface::Private::s_interface = { + destroyCallback, + getXdgOutputCallback +}; +#endif + +class XdgOutputV1Interface: public Resource +{ +public: + XdgOutputV1Interface(XdgOutputManagerInterface *parent, wl_resource *parentResource); + ~XdgOutputV1Interface(); + void setLogicalSize(const QSize &size); + void setLogicalPosition(const QPoint &pos); + void done(); +private: + class Private; +}; + +class XdgOutputInterface::Private +{ +public: + void resourceConnected(XdgOutputV1Interface *resource); + void resourceDisconnected(XdgOutputV1Interface *resource); + QPoint pos; + QSize size; + bool doneOnce = false; + QList resources; +}; + + +XdgOutputManagerInterface::XdgOutputManagerInterface(Display *display, QObject *parent) + : Global(new XdgOutputManagerInterface::Private(this, display), parent) +{ +} + +XdgOutputManagerInterface::~XdgOutputManagerInterface() +{} + +XdgOutputInterface* XdgOutputManagerInterface::createXdgOutput(OutputInterface *output, QObject *parent) +{ + Q_D(); + if (!d->outputs.contains(output)) { + auto xdgOutput = new XdgOutputInterface(parent); + d->outputs[output] = xdgOutput; + //as XdgOutput lifespan is managed by user, delete our mapping when either + //it or the relevant Output gets deleted + connect(output, &QObject::destroyed, this, [this, output]() { + Q_D(); + d->outputs.remove(output); + }); + connect(xdgOutput, &QObject::destroyed, this, [this, output]() { + Q_D(); + d->outputs.remove(output); + }); + + } + return d->outputs[output]; +} + +XdgOutputManagerInterface::Private* XdgOutputManagerInterface::d_func() const +{ + return reinterpret_cast(d.data()); +} + +void XdgOutputManagerInterface::Private::destroyCallback(wl_client *client, wl_resource *resource) +{ + Q_UNUSED(client) + wl_resource_destroy(resource); +} + +void XdgOutputManagerInterface::Private::getXdgOutputCallback(wl_client *client, wl_resource *resource, uint32_t id, wl_resource * outputResource) +{ + auto d = cast(resource); + auto output = OutputInterface::get(outputResource); + if (!output) { // output client is requesting XdgOutput for an Output that doesn't exist + return; + } + if (!d->outputs.contains(output)) { + return; //server hasn't created an XdgOutput for this output yet, give the client nothing + } + auto iface = new XdgOutputV1Interface(d->q, resource); + iface->create(d->display->getConnection(client), wl_resource_get_version(resource), id); + if (!iface->resource()) { + wl_resource_post_no_memory(resource); + delete iface; + return; + } + + auto xdgOutput = d->outputs[output]; + xdgOutput->d->resourceConnected(iface); + connect(iface, &XdgOutputV1Interface::unbound, xdgOutput, [xdgOutput, iface]() { + xdgOutput->d->resourceDisconnected(iface); + }); +} + +XdgOutputManagerInterface::Private::Private(XdgOutputManagerInterface *q, Display *d) + : Global::Private(d, &zxdg_output_manager_v1_interface, s_version) + , q(q) +{ +} + +void XdgOutputManagerInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) +{ + auto c = display->getConnection(client); + wl_resource *resource = c->createResource(&zxdg_output_manager_v1_interface, qMin(version, s_version), id); + if (!resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(resource, &s_interface, this, unbind); +} + +void XdgOutputManagerInterface::Private::unbind(wl_resource *resource) +{ + Q_UNUSED(resource) +} + +XdgOutputInterface::XdgOutputInterface(QObject *parent): + QObject(parent), + d(new XdgOutputInterface::Private) +{ +} + +XdgOutputInterface::~XdgOutputInterface() +{} + +void XdgOutputInterface::setLogicalSize(const QSize &size) +{ + if (size == d->size) { + return; + } + d->size = size; + for(auto resource: d->resources) { + resource->setLogicalSize(size); + } +} + +QSize XdgOutputInterface::logicalSize() const +{ + return d->size; +} + +void XdgOutputInterface::setLogicalPosition(const QPoint &pos) +{ + if (pos == d->pos) { + return; + } + d->pos = pos; + for(auto resource: d->resources) { + resource->setLogicalPosition(pos); + } +} + +QPoint XdgOutputInterface::logicalPosition() const +{ + return d->pos; +} + +void XdgOutputInterface::done() +{ + d->doneOnce = true; + for(auto resource: d->resources) { + resource->done(); + } +} + +void XdgOutputInterface::Private::resourceConnected(XdgOutputV1Interface *resource) +{ + resource->setLogicalPosition(pos); + resource->setLogicalSize(size); + if (doneOnce) { + resource->done(); + } + resources << resource; +} + +void XdgOutputInterface::Private::resourceDisconnected(XdgOutputV1Interface *resource) +{ + resources.removeOne(resource); +} + + +class XdgOutputV1Interface::Private : public Resource::Private +{ +public: + Private(XdgOutputV1Interface *q, XdgOutputManagerInterface *c, wl_resource *parentResource); + ~Private(); + +private: + + XdgOutputV1Interface *q_func() { + return reinterpret_cast(q); + } + + static const struct zxdg_output_v1_interface s_interface; +}; + +XdgOutputV1Interface::XdgOutputV1Interface(XdgOutputManagerInterface *parent, wl_resource *parentResource) + :Resource(new XdgOutputV1Interface::Private(this, parent, parentResource)) +{} + +XdgOutputV1Interface::~XdgOutputV1Interface() +{} + +void XdgOutputV1Interface::setLogicalSize(const QSize &size) +{ + if (!d->resource) { + return; + } + zxdg_output_v1_send_logical_size(d->resource, size.width(), size.height()); +} + +void XdgOutputV1Interface::setLogicalPosition(const QPoint &pos) +{ + if (!d->resource) { + return; + } + zxdg_output_v1_send_logical_position(d->resource, pos.x(), pos.y()); +} + +void XdgOutputV1Interface::done() +{ + if (!d->resource) { + return; + } + zxdg_output_v1_send_done(d->resource); +} + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct zxdg_output_v1_interface XdgOutputV1Interface::Private::s_interface = { + resourceDestroyedCallback +}; +#endif + +XdgOutputV1Interface::Private::Private(XdgOutputV1Interface *q, XdgOutputManagerInterface *c, wl_resource *parentResource) + : Resource::Private(q, c, parentResource, &zxdg_output_v1_interface, &s_interface) +{ +} + +XdgOutputV1Interface::Private::~Private() +{ + if (resource) { + wl_resource_destroy(resource); + resource = nullptr; + } +} + +} +} + 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 @@ -62,3 +64,5 @@ zwp_idle_inhibitor_v1;IdleInhibitor org_kde_kwin_remote_access_manager;RemoteAccessManager org_kde_kwin_remote_buffer;RemoteBuffer +zxdg_output_v1;XdgOutput +zxdg_output_manager_v1;XdgOutputManager diff --git a/tests/plasmasurfacetest.cpp b/tests/plasmasurfacetest.cpp --- a/tests/plasmasurfacetest.cpp +++ b/tests/plasmasurfacetest.cpp @@ -49,6 +49,10 @@ m_skipTaskbar = set; } + void setSkipSwitcher(bool set) { + m_skipSwitcher = set; + } + private: void setupRegistry(Registry *registry); void render(); @@ -64,6 +68,7 @@ PlasmaShellSurface *m_plasmaShellSurface = nullptr; PlasmaShellSurface::Role m_role = PlasmaShellSurface::Role::Normal; bool m_skipTaskbar = false; + bool m_skipSwitcher = false; }; PlasmaSurfaceTest::PlasmaSurfaceTest(QObject *parent) @@ -136,6 +141,7 @@ m_plasmaShellSurface = m_plasmaShell->createSurface(m_surface, this); Q_ASSERT(m_plasmaShellSurface); m_plasmaShellSurface->setSkipTaskbar(m_skipTaskbar); + m_plasmaShellSurface->setSkipSwitcher(m_skipSwitcher); m_plasmaShellSurface->setRole(m_role); render(); } @@ -177,6 +183,9 @@ QCommandLineOption skipTaskbarOption(QStringLiteral("skipTaskbar")); parser.addOption(skipTaskbarOption); parser.process(app); + QCommandLineOption skipSwitcherOption(QStringLiteral("skipSwitcher")); + parser.addOption(skipSwitcherOption); + parser.process(app); PlasmaSurfaceTest client; @@ -192,6 +201,7 @@ client.setRole(PlasmaShellSurface::Role::ToolTip); } client.setSkipTaskbar(parser.isSet(skipTaskbarOption)); + client.setSkipSwitcher(parser.isSet(skipSwitcherOption)); client.init();