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,462 @@ +/******************************************************************** +Copyright 2018 Marco Martin + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +*********************************************************************/ +// Qt +#include +// KWin +#include "../../src/client/compositor.h" +#include "../../src/client/connection_thread.h" +#include "../../src/client/event_queue.h" +#include "../../src/client/region.h" +#include "../../src/client/registry.h" +#include "../../src/client/surface.h" +#include "../../src/client/plasmavirtualdesktop.h" +#include "../../src/server/display.h" +#include "../../src/server/compositor_interface.h" +#include "../../src/server/region_interface.h" +#include "../../src/server/plasmavirtualdesktop_interface.h" +#include "../../src/server/plasmawindowmanagement_interface.h" +#include "../../src/client/plasmawindowmanagement.h" + +using namespace KWayland::Client; + +class TestVirtualDesktop : public QObject +{ + Q_OBJECT +public: + explicit TestVirtualDesktop(QObject *parent = nullptr); +private Q_SLOTS: + void init(); + void cleanup(); + + void testCreate(); + void testDestroy(); + void testActivate(); + + void testEnterLeaveDesktop(); + void testAllDesktops(); + +private: + KWayland::Server::Display *m_display; + KWayland::Server::CompositorInterface *m_compositorInterface; + KWayland::Server::PlasmaVirtualDesktopManagementInterface *m_plasmaVirtualDesktopManagementInterface; + KWayland::Server::PlasmaWindowManagementInterface *m_windowManagementInterface; + KWayland::Server::PlasmaWindowInterface *m_windowInterface; + + KWayland::Client::ConnectionThread *m_connection; + KWayland::Client::Compositor *m_compositor; + KWayland::Client::PlasmaVirtualDesktopManagement *m_plasmaVirtualDesktopManagement; + KWayland::Client::EventQueue *m_queue; + KWayland::Client::PlasmaWindowManagement *m_windowManagement; + KWayland::Client::PlasmaWindow *m_window; + + QThread *m_thread; +}; + +static const QString s_socketName = QStringLiteral("kwayland-test-wayland-virtual-desktop-0"); + +TestVirtualDesktop::TestVirtualDesktop(QObject *parent) + : QObject(parent) + , m_display(nullptr) + , m_compositorInterface(nullptr) + , m_connection(nullptr) + , m_compositor(nullptr) + , m_queue(nullptr) + , m_thread(nullptr) +{ +} + +void TestVirtualDesktop::init() +{ + using namespace KWayland::Server; + delete m_display; + m_display = new Display(this); + m_display->setSocketName(s_socketName); + m_display->start(); + QVERIFY(m_display->isRunning()); + + // setup connection + m_connection = new KWayland::Client::ConnectionThread; + QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); + QVERIFY(connectedSpy.isValid()); + m_connection->setSocketName(s_socketName); + + m_thread = new QThread(this); + m_connection->moveToThread(m_thread); + m_thread->start(); + + m_connection->initConnection(); + QVERIFY(connectedSpy.wait()); + + m_queue = new KWayland::Client::EventQueue(this); + QVERIFY(!m_queue->isValid()); + m_queue->setup(m_connection); + QVERIFY(m_queue->isValid()); + + Registry registry; + QSignalSpy compositorSpy(®istry, &Registry::compositorAnnounced); + QVERIFY(compositorSpy.isValid()); + + QSignalSpy plasmaVirtualDesktopManagementSpy(®istry, &Registry::plasmaVirtualDesktopManagementAnnounced); + QVERIFY(plasmaVirtualDesktopManagementSpy.isValid()); + + QSignalSpy windowManagementSpy(®istry, SIGNAL(plasmaWindowManagementAnnounced(quint32,quint32))); + QVERIFY(windowManagementSpy.isValid()); + + QVERIFY(!registry.eventQueue()); + registry.setEventQueue(m_queue); + QCOMPARE(registry.eventQueue(), m_queue); + registry.create(m_connection->display()); + QVERIFY(registry.isValid()); + registry.setup(); + + m_compositorInterface = m_display->createCompositor(m_display); + m_compositorInterface->create(); + QVERIFY(m_compositorInterface->isValid()); + + QVERIFY(compositorSpy.wait()); + m_compositor = registry.createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); + + m_plasmaVirtualDesktopManagementInterface = m_display->createPlasmaVirtualDesktopManagement(m_display); + m_plasmaVirtualDesktopManagementInterface->create(); + QVERIFY(m_plasmaVirtualDesktopManagementInterface->isValid()); + + QVERIFY(plasmaVirtualDesktopManagementSpy.wait()); + m_plasmaVirtualDesktopManagement = registry.createPlasmaVirtualDesktopManagement(plasmaVirtualDesktopManagementSpy.first().first().value(), plasmaVirtualDesktopManagementSpy.first().last().value(), this); + + m_windowManagementInterface = m_display->createPlasmaWindowManagement(m_display); + m_windowManagementInterface->create(); + QVERIFY(m_windowManagementInterface->isValid()); + m_windowManagementInterface->setPlasmaVirtualDesktopManagementInterface(m_plasmaVirtualDesktopManagementInterface); + + QVERIFY(windowManagementSpy.wait()); + m_windowManagement = registry.createPlasmaWindowManagement(windowManagementSpy.first().first().value(), windowManagementSpy.first().last().value(), this); + + QSignalSpy windowSpy(m_windowManagement, SIGNAL(windowCreated(KWayland::Client::PlasmaWindow *))); + QVERIFY(windowSpy.isValid()); + m_windowInterface = m_windowManagementInterface->createWindow(this); + m_windowInterface->setPid(1337); + + QVERIFY(windowSpy.wait()); + m_window = windowSpy.first().first().value(); +} + +void TestVirtualDesktop::cleanup() +{ +#define CLEANUP(variable) \ + if (variable) { \ + delete variable; \ + variable = nullptr; \ + } + CLEANUP(m_compositor) + CLEANUP(m_plasmaVirtualDesktopManagement) + CLEANUP(m_windowManagement) + CLEANUP(m_queue) + if (m_connection) { + m_connection->deleteLater(); + m_connection = nullptr; + } + if (m_thread) { + m_thread->quit(); + m_thread->wait(); + delete m_thread; + m_thread = nullptr; + } + CLEANUP(m_compositorInterface) + CLEANUP(m_plasmaVirtualDesktopManagementInterface) + CLEANUP(m_windowManagementInterface) + CLEANUP(m_display) +#undef CLEANUP +} + +void TestVirtualDesktop::testCreate() +{ + QSignalSpy desktopAddedSpy(m_plasmaVirtualDesktopManagement, &PlasmaVirtualDesktopManagement::desktopAdded); + QSignalSpy managementDoneSpy(m_plasmaVirtualDesktopManagement, &PlasmaVirtualDesktopManagement::done); + + + //on this createDesktop bind() isn't called already, the desktopadded signals will be sent after bind happened + KWayland::Server::PlasmaVirtualDesktopInterface *desktop1Int = m_plasmaVirtualDesktopManagementInterface->createDesktop(QStringLiteral("0-1")); + desktop1Int->setName("Desktop 1"); + + desktopAddedSpy.wait(); + QList arguments = desktopAddedSpy.takeFirst(); + QCOMPARE(arguments.at(0).toString(), QStringLiteral("0-1")); + QCOMPARE(arguments.at(1).toUInt(), (quint32)0); + m_plasmaVirtualDesktopManagementInterface->sendDone(); + managementDoneSpy.wait(); + + + QCOMPARE(m_plasmaVirtualDesktopManagement->desktops().length(), 1); + + KWayland::Client::PlasmaVirtualDesktop *desktop1 = m_plasmaVirtualDesktopManagement->desktops().first(); + QSignalSpy desktop1DoneSpy(desktop1, &PlasmaVirtualDesktop::done); + desktop1Int->sendDone(); + desktop1DoneSpy.wait(); + + QCOMPARE(desktop1->id(), QStringLiteral("0-1")); + QCOMPARE(desktop1->name(), QStringLiteral("Desktop 1")); + + + //on those createDesktop the bind will already be done + KWayland::Server::PlasmaVirtualDesktopInterface *desktop2Int = m_plasmaVirtualDesktopManagementInterface->createDesktop(QStringLiteral("0-2")); + desktop2Int->setName("Desktop 2"); + desktopAddedSpy.wait(); + arguments = desktopAddedSpy.takeFirst(); + QCOMPARE(arguments.at(0).toString(), QStringLiteral("0-2")); + QCOMPARE(arguments.at(1).toUInt(), (quint32)1); + QCOMPARE(m_plasmaVirtualDesktopManagement->desktops().length(), 2); + + KWayland::Server::PlasmaVirtualDesktopInterface *desktop3Int = m_plasmaVirtualDesktopManagementInterface->createDesktop(QStringLiteral("0-3")); + desktop3Int->setName("Desktop 3"); + desktopAddedSpy.wait(); + arguments = desktopAddedSpy.takeFirst(); + QCOMPARE(arguments.at(0).toString(), QStringLiteral("0-3")); + QCOMPARE(m_plasmaVirtualDesktopManagement->desktops().length(), 3); + + m_plasmaVirtualDesktopManagementInterface->sendDone(); + managementDoneSpy.wait(); + + + //get the clients + KWayland::Client::PlasmaVirtualDesktop *desktop2 = m_plasmaVirtualDesktopManagement->desktops()[1]; + QSignalSpy desktop2DoneSpy(desktop2, &PlasmaVirtualDesktop::done); + desktop2Int->sendDone(); + desktop2DoneSpy.wait(); + + KWayland::Client::PlasmaVirtualDesktop *desktop3 = m_plasmaVirtualDesktopManagement->desktops()[2]; + QSignalSpy desktop3DoneSpy(desktop3, &PlasmaVirtualDesktop::done); + desktop3Int->sendDone(); + desktop3DoneSpy.wait(); + + + QCOMPARE(desktop1->id(), QStringLiteral("0-1")); + QCOMPARE(desktop1->name(), QStringLiteral("Desktop 1")); + + QCOMPARE(desktop2->id(), QStringLiteral("0-2")); + QCOMPARE(desktop2->name(), QStringLiteral("Desktop 2")); + + QCOMPARE(desktop3->id(), QStringLiteral("0-3")); + QCOMPARE(desktop3->name(), QStringLiteral("Desktop 3")); + + //coherence of order between client and server + QCOMPARE(m_plasmaVirtualDesktopManagementInterface->desktops().length(), 3); + QCOMPARE(m_plasmaVirtualDesktopManagement->desktops().length(), 3); + + for (int i = 0; i < m_plasmaVirtualDesktopManagement->desktops().length(); ++i) { + QCOMPARE(m_plasmaVirtualDesktopManagementInterface->desktops().at(i)->id(), m_plasmaVirtualDesktopManagement->desktops().at(i)->id()); + } +} + +void TestVirtualDesktop::testDestroy() +{ + //rebuild some desktops + testCreate(); + + KWayland::Server::PlasmaVirtualDesktopInterface *desktop1Int = m_plasmaVirtualDesktopManagementInterface->desktops().first(); + KWayland::Client::PlasmaVirtualDesktop *desktop1 = m_plasmaVirtualDesktopManagement->desktops().first(); + + + QSignalSpy desktop1IntDestroyedSpy(desktop1Int, &QObject::destroyed); + QSignalSpy desktop1DestroyedSpy(desktop1, &QObject::destroyed); + QSignalSpy desktop1RemovedSpy(desktop1, &KWayland::Client::PlasmaVirtualDesktop::removed); + m_plasmaVirtualDesktopManagementInterface->removeDesktop(QStringLiteral("0-1")); + + //test that both server and client desktoip interfaces go away + desktop1IntDestroyedSpy.wait(); + desktop1RemovedSpy.wait(); + desktop1DestroyedSpy.wait(); + + //coherence of order between client and server + QCOMPARE(m_plasmaVirtualDesktopManagementInterface->desktops().length(), 2); + QCOMPARE(m_plasmaVirtualDesktopManagement->desktops().length(), 2); + + for (int i = 0; i < m_plasmaVirtualDesktopManagement->desktops().length(); ++i) { + QCOMPARE(m_plasmaVirtualDesktopManagementInterface->desktops().at(i)->id(), m_plasmaVirtualDesktopManagement->desktops().at(i)->id()); + } + + //Test 0-2 is active + QVERIFY(m_plasmaVirtualDesktopManagement->desktops().first()->active()); + QVERIFY(m_plasmaVirtualDesktopManagementInterface->desktops().first()->active()); + + //Test the desktopRemoved signal of the manager, remove another desktop as the signals can't be tested at the same time + QSignalSpy desktopManagerRemovedSpy(m_plasmaVirtualDesktopManagement, &KWayland::Client::PlasmaVirtualDesktopManagement::desktopRemoved); + m_plasmaVirtualDesktopManagementInterface->removeDesktop(QStringLiteral("0-2")); + desktopManagerRemovedSpy.wait(); + QCOMPARE(desktopManagerRemovedSpy.takeFirst().at(0).toString(), QStringLiteral("0-2")); + + QCOMPARE(m_plasmaVirtualDesktopManagementInterface->desktops().length(), 1); + QCOMPARE(m_plasmaVirtualDesktopManagement->desktops().length(), 1); + + //Test 0-3 is active + QVERIFY(m_plasmaVirtualDesktopManagement->desktops().first()->active()); + QVERIFY(m_plasmaVirtualDesktopManagementInterface->desktops().first()->active()); +} + +void TestVirtualDesktop::testActivate() +{ + //rebuild some desktops + testCreate(); + + KWayland::Server::PlasmaVirtualDesktopInterface *desktop1Int = m_plasmaVirtualDesktopManagementInterface->desktops().first(); + KWayland::Client::PlasmaVirtualDesktop *desktop1 = m_plasmaVirtualDesktopManagement->desktops().first(); + QVERIFY(desktop1->active()); + QVERIFY(desktop1Int->active()); + + KWayland::Server::PlasmaVirtualDesktopInterface *desktop2Int = m_plasmaVirtualDesktopManagementInterface->desktops()[1]; + KWayland::Client::PlasmaVirtualDesktop *desktop2 = m_plasmaVirtualDesktopManagement->desktops()[1]; + QVERIFY(!desktop2Int->active()); + + QSignalSpy requestActivateSpy(desktop2Int, &KWayland::Server::PlasmaVirtualDesktopInterface::activateRequested); + QSignalSpy activatedSpy(desktop2, &KWayland::Client::PlasmaVirtualDesktop::activated); + + desktop2->requestActivate(); + requestActivateSpy.wait(); + + //activate the desktop that was requested active + m_plasmaVirtualDesktopManagementInterface->setActiveDesktop(desktop2->id()); + activatedSpy.wait(); + + //correct state in the server + QVERIFY(desktop2Int->active()); + QVERIFY(!desktop1Int->active()); + //correct state in the client + QVERIFY(desktop2Int->active()); + QVERIFY(!desktop1Int->active()); + + //Test the deactivated signal + QSignalSpy deactivatedSpy(desktop2, &KWayland::Client::PlasmaVirtualDesktop::deactivated); + m_plasmaVirtualDesktopManagementInterface->setActiveDesktop(desktop1->id()); + deactivatedSpy.wait(); +} + +void TestVirtualDesktop::testEnterLeaveDesktop() +{ + testCreate(); + + QSignalSpy enterRequestedSpy(m_windowInterface, &KWayland::Server::PlasmaWindowInterface::enterPlasmaVirtualDesktopRequested); + m_window->requestEnterVirtualDesktop(QStringLiteral("0-1")); + enterRequestedSpy.wait(); + + QCOMPARE(enterRequestedSpy.takeFirst().at(0).toString(), QStringLiteral("0-1")); + + QSignalSpy virtualDesktopEnteredSpy(m_window, &KWayland::Client::PlasmaWindow::plasmaVirtualDesktopEntered); + + //agree to the request + m_windowInterface->addPlasmaVirtualDesktop(QStringLiteral("0-1")); + QCOMPARE(m_windowInterface->plasmaVirtualDesktops().length(), 1); + QCOMPARE(m_windowInterface->plasmaVirtualDesktops().first(), QStringLiteral("0-1")); + + //check if the client received the enter + virtualDesktopEnteredSpy.wait(); + QCOMPARE(virtualDesktopEnteredSpy.takeFirst().at(0).toString(), QStringLiteral("0-1")); + QCOMPARE(m_window->plasmaVirtualDesktops().length(), 1); + QCOMPARE(m_window->plasmaVirtualDesktops().first(), QStringLiteral("0-1")); + + //add another desktop, server side + m_windowInterface->addPlasmaVirtualDesktop(QStringLiteral("0-3")); + virtualDesktopEnteredSpy.wait(); + QCOMPARE(virtualDesktopEnteredSpy.takeFirst().at(0).toString(), QStringLiteral("0-3")); + QCOMPARE(m_windowInterface->plasmaVirtualDesktops().length(), 2); + QCOMPARE(m_window->plasmaVirtualDesktops().length(), 2); + QCOMPARE(m_window->plasmaVirtualDesktops()[1], QStringLiteral("0-3")); + + + + //try to add an invalid desktop + m_windowInterface->addPlasmaVirtualDesktop(QStringLiteral("invalid")); + QCOMPARE(m_window->plasmaVirtualDesktops().length(), 2); + + //remove a desktop + QSignalSpy leaveRequestedSpy(m_windowInterface, &KWayland::Server::PlasmaWindowInterface::leavePlasmaVirtualDesktopRequested); + m_window->requestLeaveVirtualDesktop(QStringLiteral("0-1")); + leaveRequestedSpy.wait(); + + QCOMPARE(leaveRequestedSpy.takeFirst().at(0).toString(), QStringLiteral("0-1")); + + QSignalSpy virtualDesktopLeftSpy(m_window, &KWayland::Client::PlasmaWindow::plasmaVirtualDesktopLeft); + + //agree to the request + m_windowInterface->removePlasmaVirtualDesktop(QStringLiteral("0-1")); + QCOMPARE(m_windowInterface->plasmaVirtualDesktops().length(), 1); + QCOMPARE(m_windowInterface->plasmaVirtualDesktops().first(), QStringLiteral("0-3")); + + //check if the client received the leave + virtualDesktopLeftSpy.wait(); + QCOMPARE(virtualDesktopLeftSpy.takeFirst().at(0).toString(), QStringLiteral("0-1")); + QCOMPARE(m_window->plasmaVirtualDesktops().length(), 1); + QCOMPARE(m_window->plasmaVirtualDesktops().first(), QStringLiteral("0-3")); + + //Destroy desktop 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()); +} + + +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.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.xml + BASENAME plasma-virtual-desktop +) + ecm_add_wayland_client_protocol(CLIENT_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/plasma-window-management.xml BASENAME plasma-window-management @@ -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.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/plasmavirtualdesktop.h b/src/client/plasmavirtualdesktop.h new file mode 100644 --- /dev/null +++ b/src/client/plasmavirtualdesktop.h @@ -0,0 +1,267 @@ +/**************************************************************************** +Copyright 2018 Marco Martin + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +****************************************************************************/ +#ifndef KWAYLAND_CLIENT_PLASMAVIRTUALDESKTOP_H +#define KWAYLAND_CLIENT_PLASMAVIRTUALDESKTOP_H + +#include + +#include + +struct org_kde_plasma_virtual_desktop_management; +struct org_kde_plasma_virtual_desktop; + +namespace KWayland +{ +namespace Client +{ + +class EventQueue; +class PlasmaVirtualDesktop; + +/** + * @short Wrapper for the org_kde_plasma_virtual_desktop_management interface. + * + * This class provides a convenient wrapper for the org_kde_plasma_virtual_desktop_management interface. + * + * To use this class one needs to interact with the Registry. There are two + * possible ways to create the PlasmaVirtualDesktopManagement interface: + * @code + * PlasmaVirtualDesktopManagement *c = registry->createPlasmaVirtualDesktopManagement(name, version); + * @endcode + * + * This creates the PlasmaVirtualDesktopManagement and sets it up directly. As an alternative this + * can also be done in a more low level way: + * @code + * PlasmaVirtualDesktopManagement *c = new PlasmaVirtualDesktopManagement; + * c->setup(registry->bindPlasmaVirtualDesktopManagement(name, version)); + * @endcode + * + * The PlasmaVirtualDesktopManagement can be used as a drop-in replacement for any org_kde_plasma_virtual_desktop_management + * pointer as it provides matching cast operators. + * @since 5.46 + * + * @see Registry + **/ +class KWAYLANDCLIENT_EXPORT PlasmaVirtualDesktopManagement : public QObject +{ + Q_OBJECT +public: + /** + * Creates a new PlasmaVirtualDesktopManagement. + * Note: after constructing the PlasmaVirtualDesktopManagement it is not yet valid and one needs + * to call setup. In order to get a ready to use PlasmaVirtualDesktopManagement prefer using + * Registry::createPlasmaVirtualDesktopManagement. + **/ + explicit PlasmaVirtualDesktopManagement(QObject *parent = nullptr); + virtual ~PlasmaVirtualDesktopManagement(); + + /** + * Setup this PlasmaVirtualDesktopManagement to manage the @p plasmavirtualdesktopmanagement. + * When using Registry::createPlasmaVirtualDesktopManagement there is no need to call this + * method. + **/ + void setup(org_kde_plasma_virtual_desktop_management *plasmavirtualdesktopmanagement); + /** + * @returns @c true if managing a org_kde_plasma_virtual_desktop_management. + **/ + bool isValid() const; + /** + * Releases the org_kde_plasma_virtual_desktop_management interface. + * After the interface has been released the PlasmaVirtualDesktopManagement instance is no + * longer valid and can be setup with another org_kde_plasma_virtual_desktop_management interface. + **/ + void release(); + /** + * Destroys the data held by this PlasmaVirtualDesktopManagement. + * This method is supposed to be used when the connection to the Wayland + * server goes away. If the connection is not valid anymore, it's not + * possible to call release anymore as that calls into the Wayland + * connection and the call would fail. This method cleans up the data, so + * that the instance can be deleted or set up to a new org_kde_plasma_virtual_desktop_management interface + * once there is a new connection available. + * + * It is suggested to connect this method to ConnectionThread::connectionDied: + * @code + * connect(connection, &ConnectionThread::connectionDied, plasmavirtualdesktopmanagement, &PlasmaVirtualDesktopManagement::destroy); + * @endcode + * + * @see release + **/ + void destroy(); + + /** + * Sets the @p queue to use for creating objects with this PlasmaVirtualDesktopManagement. + **/ + void setEventQueue(EventQueue *queue); + + /** + * @returns The event queue to use for creating objects with this PlasmaVirtualDesktopManagement. + * The object is owned by the manager and the caller should not delete it. + **/ + EventQueue *eventQueue(); + + PlasmaVirtualDesktop *getVirtualDesktop(const QString &id); + + /** + * @returns All the existent virtual desktops + */ + QList desktops() const; + + operator org_kde_plasma_virtual_desktop_management*(); + operator org_kde_plasma_virtual_desktop_management*() const; + +Q_SIGNALS: + void removed(); + + /** + * Emitted when a new desktop has been added + */ + void desktopAdded(const QString &id, quint32 position); + + /** + * Emitted when a desktop has been removed + */ + void desktopRemoved(const QString &id); + + /** + * This event is sent after all other properties has been + * sent after binding to the desktop manager object and after any + * other property changes done after that. This allows + * changes to the org_kde_plasma_virtual_desktop_management properties + * to be seen as atomic, even if they happen via multiple events. + */ + void done(); + +private: + class Private; + QScopedPointer d; +}; + +class KWAYLANDCLIENT_EXPORT PlasmaVirtualDesktop : public QObject +{ + Q_OBJECT +public: + virtual ~PlasmaVirtualDesktop(); + + /** + * Setup this PlasmaVirtualDesktop to manage the @p plasmavirtualdesktop. + * When using PlasmaVirtualDesktopManagement::createPlasmaVirtualDesktop there is no need to call this + * method. + **/ + void setup(org_kde_plasma_virtual_desktop *plasmavirtualdesktop); + + /** + * @returns @c true if managing a org_kde_plasma_virtual_desktop. + **/ + bool isValid() const; + + /** + * Releases the org_kde_plasma_virtual_desktop interface. + * After the interface has been released the PlasmaVirtualDesktop instance is no + * longer valid and can be setup with another org_kde_plasma_virtual_desktop interface. + **/ + void release(); + + /** + * Destroys the data held by this PlasmaVirtualDesktop. + * This method is supposed to be used when the connection to the Wayland + * server goes away. If the connection is not valid anymore, it's not + * possible to call release anymore as that calls into the Wayland + * connection and the call would fail. This method cleans up the data, so + * that the instance can be deleted or set up to a new org_kde_plasma_virtual_desktop interface + * once there is a new connection available. + * + * It is suggested to connect this method to ConnectionThread::connectionDied: + * @code + * connect(connection, &ConnectionThread::connectionDied, plasmavirtualdesktop, &PlasmaVirtualDesktop::destroy); + * @endcode + * + * @see release + **/ + void destroy(); + + /** + * Requests this desktop to be activated. + * The server may or may not decide to consent to the request. + */ + void requestActivate(); + + /** + * @returns The unique id of this desktop. The format of the id is decided by the compositor + */ + QString id() const; + + + /** + * @returns User readable name for the desktop. + */ + QString name() const; + + /** + * @returns True if the desktop is the active one. + * when this property changes, activated or deactivated will be emitted. + * @see activated + * @see deactivated + */ + bool active() const; + + operator org_kde_plasma_virtual_desktop*(); + operator org_kde_plasma_virtual_desktop*() const; + +Q_SIGNALS: + /** + * TODO: activeChanged(bool)? + * Emitted when this desktop has been activated by the server + */ + void activated(); + + /** + * Emitted when this desktop has been activated by the server + */ + void deactivated(); + + /** + * This event is sent after all other properties has been + * sent after binding to the desktop manager object and after any + * other property changes done after that. This allows + * changes to the org_kde_plasma_virtual_desktop properties + * to be seen as atomic, even if they happen via multiple events. + */ + void done(); + + /** + * This virtual desktop has just been removed by the server: + * This object itself is about to be deleted. All windows will + * lose the association to this desktop. + */ + void removed(); + +private: + friend class PlasmaVirtualDesktopManagement; + explicit PlasmaVirtualDesktop(QObject *parent = nullptr); + class Private; + QScopedPointer d; +}; + + +} +} + +#endif diff --git a/src/client/plasmavirtualdesktop.cpp b/src/client/plasmavirtualdesktop.cpp new file mode 100644 --- /dev/null +++ b/src/client/plasmavirtualdesktop.cpp @@ -0,0 +1,367 @@ +/**************************************************************************** +Copyright 2018 Marco Martin + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +****************************************************************************/ +#include "plasmavirtualdesktop.h" +#include "event_queue.h" +#include "wayland_pointer_p.h" + +#include +#include + +#include + +namespace KWayland +{ +namespace Client +{ + +class Q_DECL_HIDDEN PlasmaVirtualDesktopManagement::Private +{ +public: + Private(PlasmaVirtualDesktopManagement *q); + + void setup(org_kde_plasma_virtual_desktop_management *arg); + + WaylandPointer plasmavirtualdesktopmanagement; + EventQueue *queue = nullptr; + + QList desktops; + + inline QList::const_iterator constFindDesktop(const QString &id); + inline QList::iterator findDesktop(const QString &id); + +private: + static void addedCallback(void *data, org_kde_plasma_virtual_desktop_management *org_kde_plasma_virtual_desktop_management, const char *id, uint32_t position); + static void removedCallback(void *data, org_kde_plasma_virtual_desktop_management *org_kde_plasma_virtual_desktop_management, const char *id); + static void doneCallback(void *data, org_kde_plasma_virtual_desktop_management *org_kde_plasma_virtual_desktop_management); + + PlasmaVirtualDesktopManagement *q; + + static const org_kde_plasma_virtual_desktop_management_listener s_listener; +}; + +inline QList::const_iterator PlasmaVirtualDesktopManagement::Private::constFindDesktop(const QString &id) +{ + return std::find_if( desktops.constBegin(), + desktops.constEnd(), + [id]( const PlasmaVirtualDesktop *desk ){ return desk->id() == id; } ); +} + +inline QList::iterator PlasmaVirtualDesktopManagement::Private::findDesktop(const QString &id) +{ + return std::find_if( desktops.begin(), + desktops.end(), + [id]( const PlasmaVirtualDesktop *desk ){ return desk->id() == id; } ); +} + +const org_kde_plasma_virtual_desktop_management_listener PlasmaVirtualDesktopManagement::Private::s_listener = { + addedCallback, + removedCallback, + doneCallback +}; + +void PlasmaVirtualDesktopManagement::Private::addedCallback(void *data, org_kde_plasma_virtual_desktop_management *org_kde_plasma_virtual_desktop_management, const char *id, uint32_t position) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->plasmavirtualdesktopmanagement == org_kde_plasma_virtual_desktop_management); + const QString stringId = QString::fromUtf8(id); + PlasmaVirtualDesktop *vd = p->q->getVirtualDesktop(stringId); + Q_ASSERT(vd); + + p->desktops.insert(position, vd); + //TODO: emit a lot of desktopMoved? + + emit p->q->desktopAdded(stringId, position); +} + +void PlasmaVirtualDesktopManagement::Private::removedCallback(void *data, org_kde_plasma_virtual_desktop_management *org_kde_plasma_virtual_desktop_management, const char *id) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->plasmavirtualdesktopmanagement == org_kde_plasma_virtual_desktop_management); + const QString stringId = QString::fromUtf8(id); + PlasmaVirtualDesktop *vd = p->q->getVirtualDesktop(stringId); + //TODO: emit a lot of desktopMoved? + Q_ASSERT(vd); + auto i = p->findDesktop(stringId); + p->desktops.erase(i); + vd->release(); + vd->destroy(); + vd->deleteLater(); + emit p->q->desktopRemoved(stringId); +} + +void PlasmaVirtualDesktopManagement::Private::doneCallback(void *data, org_kde_plasma_virtual_desktop_management *org_kde_plasma_virtual_desktop_management) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->plasmavirtualdesktopmanagement == org_kde_plasma_virtual_desktop_management); + emit p->q->done(); +} + +PlasmaVirtualDesktopManagement::PlasmaVirtualDesktopManagement(QObject *parent) + : QObject(parent) + , d(new Private(this)) +{ +} + +PlasmaVirtualDesktopManagement::Private::Private(PlasmaVirtualDesktopManagement *q) + : q(q) +{} + +void PlasmaVirtualDesktopManagement::Private::setup(org_kde_plasma_virtual_desktop_management *arg) +{ + Q_ASSERT(arg); + Q_ASSERT(!plasmavirtualdesktopmanagement); + plasmavirtualdesktopmanagement.setup(arg); + org_kde_plasma_virtual_desktop_management_add_listener(plasmavirtualdesktopmanagement, &s_listener, this); +} + +PlasmaVirtualDesktopManagement::~PlasmaVirtualDesktopManagement() +{ + release(); +} + +void PlasmaVirtualDesktopManagement::setup(org_kde_plasma_virtual_desktop_management *plasmavirtualdesktopmanagement) +{ + d->setup(plasmavirtualdesktopmanagement); +} + +void PlasmaVirtualDesktopManagement::release() +{ + d->plasmavirtualdesktopmanagement.release(); +} + +void PlasmaVirtualDesktopManagement::destroy() +{ + d->plasmavirtualdesktopmanagement.destroy(); +} + +PlasmaVirtualDesktopManagement::operator org_kde_plasma_virtual_desktop_management*() { + return d->plasmavirtualdesktopmanagement; +} + +PlasmaVirtualDesktopManagement::operator org_kde_plasma_virtual_desktop_management*() const { + return d->plasmavirtualdesktopmanagement; +} + +bool PlasmaVirtualDesktopManagement::isValid() const +{ + return d->plasmavirtualdesktopmanagement.isValid(); +} + +void PlasmaVirtualDesktopManagement::setEventQueue(EventQueue *queue) +{ + d->queue = queue; +} + +EventQueue *PlasmaVirtualDesktopManagement::eventQueue() +{ + return d->queue; +} + +PlasmaVirtualDesktop *PlasmaVirtualDesktopManagement::getVirtualDesktop(const QString &id) +{ + Q_ASSERT(isValid()); + + if (id.isEmpty()) { + return nullptr; + } + + auto i = d->constFindDesktop(id); + if (i != d->desktops.constEnd()) { + return *i; + } + + auto w = org_kde_plasma_virtual_desktop_management_get_virtual_desktop(d->plasmavirtualdesktopmanagement, id.toUtf8()); + + if (!w) { + return nullptr; + } + + if (d->queue) { + d->queue->addProxy(w); + } + + auto desktop = new PlasmaVirtualDesktop(this); + desktop->setup(w); + + return desktop; +} + +QList PlasmaVirtualDesktopManagement::desktops() const +{ + return d->desktops; +} + +class Q_DECL_HIDDEN PlasmaVirtualDesktop::Private +{ +public: + Private(PlasmaVirtualDesktop *q); + + void setup(org_kde_plasma_virtual_desktop *arg); + + WaylandPointer plasmavirtualdesktop; + + QString id; + QString name; + bool active = false; + +private: + PlasmaVirtualDesktop *q; + +private: + static void idCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop, const char * id); + static void nameCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop, const char * name); + + static void activatedCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop); + static void deactivatedCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop); + static void doneCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop); + static void removedCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop); + + static const org_kde_plasma_virtual_desktop_listener s_listener; +}; + +const org_kde_plasma_virtual_desktop_listener PlasmaVirtualDesktop::Private::s_listener = { + idCallback, + nameCallback, + activatedCallback, + deactivatedCallback, + doneCallback, + removedCallback +}; + +void PlasmaVirtualDesktop::Private::idCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop, const char * id) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->plasmavirtualdesktop == org_kde_plasma_virtual_desktop); + p->id = QString::fromUtf8(id); +} + +void PlasmaVirtualDesktop::Private::nameCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop, const char * name) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->plasmavirtualdesktop == org_kde_plasma_virtual_desktop); + p->name = QString::fromUtf8(name); +} + +void PlasmaVirtualDesktop::Private::activatedCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->plasmavirtualdesktop == org_kde_plasma_virtual_desktop); + p->active = true; + emit p->q->activated(); +} + +void PlasmaVirtualDesktop::Private::deactivatedCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->plasmavirtualdesktop == org_kde_plasma_virtual_desktop); + p->active = false; + emit p->q->deactivated(); +} + +void PlasmaVirtualDesktop::Private::doneCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->plasmavirtualdesktop == org_kde_plasma_virtual_desktop); + emit p->q->done(); +} + +void PlasmaVirtualDesktop::Private::removedCallback(void *data, org_kde_plasma_virtual_desktop *org_kde_plasma_virtual_desktop) +{ + auto p = reinterpret_cast(data); + Q_ASSERT(p->plasmavirtualdesktop == org_kde_plasma_virtual_desktop); + emit p->q->removed(); +} + +PlasmaVirtualDesktop::Private::Private(PlasmaVirtualDesktop *q) + : q(q) +{ +} + +PlasmaVirtualDesktop::PlasmaVirtualDesktop(QObject *parent) + : QObject(parent) + , d(new Private(this)) +{ +} + +void PlasmaVirtualDesktop::Private::setup(org_kde_plasma_virtual_desktop *arg) +{ + Q_ASSERT(arg); + Q_ASSERT(!plasmavirtualdesktop); + plasmavirtualdesktop.setup(arg); + org_kde_plasma_virtual_desktop_add_listener(plasmavirtualdesktop, &s_listener, this); +} + +PlasmaVirtualDesktop::~PlasmaVirtualDesktop() +{ + release(); +} + +void PlasmaVirtualDesktop::setup(org_kde_plasma_virtual_desktop *plasmavirtualdesktop) +{ + d->setup(plasmavirtualdesktop); +} + +void PlasmaVirtualDesktop::release() +{ + d->plasmavirtualdesktop.release(); +} + +void PlasmaVirtualDesktop::destroy() +{ + d->plasmavirtualdesktop.destroy(); +} + +PlasmaVirtualDesktop::operator org_kde_plasma_virtual_desktop*() { + return d->plasmavirtualdesktop; +} + +PlasmaVirtualDesktop::operator org_kde_plasma_virtual_desktop*() const { + return d->plasmavirtualdesktop; +} + +bool PlasmaVirtualDesktop::isValid() const +{ + return d->plasmavirtualdesktop.isValid(); +} + +void PlasmaVirtualDesktop::requestActivate() +{ + Q_ASSERT(isValid()); + org_kde_plasma_virtual_desktop_request_activate(d->plasmavirtualdesktop); +} + +QString PlasmaVirtualDesktop::id() const +{ + return d->id; +} + +QString PlasmaVirtualDesktop::name() const +{ + return d->name; +} + +bool PlasmaVirtualDesktop::active() const +{ + return d->active; +} + +} +} + diff --git a/src/client/plasmawindowmanagement.h b/src/client/plasmawindowmanagement.h --- a/src/client/plasmawindowmanagement.h +++ b/src/client/plasmawindowmanagement.h @@ -37,6 +37,7 @@ class PlasmaWindow; class PlasmaWindowModel; class Surface; +class PlasmaVirtualDesktop; /** * @short Wrapper for the org_kde_plasma_window_management interface. @@ -272,6 +273,8 @@ **/ QString appId() const; /** + * @deprecated: use plasmaVirtualDesktops instead + * @see plasmaVirtualDesktops * @returns the id of the virtual desktop this PlasmaWindow is on * @see virtualDesktopChanged **/ @@ -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,33 @@ **/ QRect geometry() const; + /** + * Ask the server to make the window enter a virtual desktop. + * The server may or may not consent. + * A window can enter more than one virtual desktop. + * + * @since 5.46 + */ + void requestEnterVirtualDesktop(const QString &id); + + /** + * Ask the server to make the window the window exit a virtual desktop. + * The server may or may not consent. + * If it exits all desktops it will be considered on all of them. + * + * @since 5.46 + */ + void requestLeaveVirtualDesktop(const QString &id); + + /** + * Return all the virtual desktop ids this window is associated to. + * When a desktop gets deleted, it will be automatically removed from this list. + * If this list is empty, assume it's on all desktops. + * + * @since 5.46 + */ + QStringList plasmaVirtualDesktops() const; + Q_SIGNALS: /** * The window title changed. @@ -487,8 +523,8 @@ **/ void appIdChanged(); /** + * @deprecated use plasmaVirtualDesktopEntered and plasmaVirtualDesktopLeft instead * The virtual desktop changed. - * @see virtualDesktop **/ void virtualDesktopChanged(); /** @@ -557,6 +593,11 @@ **/ void skipTaskbarChanged(); /** + * The skip switcher state changed. + * @see skipSwitcher + **/ + void skipSwitcherChanged(); + /** * The window icon changed. * @see icon **/ @@ -610,6 +651,21 @@ **/ void geometryChanged(); + /** + * This signal is emitted when the window has entered a new virtual desktop. + * The window can be on more than one desktop, or none: then is considered on all of them. + * @since 5.46 + */ + void plasmaVirtualDesktopEntered(const QString &id); + + /** + * This signal is emitted when the window left a virtual desktop. + * If the window leaves all desktops, it can be considered on all. + * + * @since 5.46 + */ + void plasmaVirtualDesktopLeft(const QString &id); + private: friend class PlasmaWindowManagement; explicit PlasmaWindow(PlasmaWindowManagement *parent, org_kde_plasma_window *dataOffer, quint32 internalId); diff --git a/src/client/plasmawindowmanagement.cpp b/src/client/plasmawindowmanagement.cpp --- a/src/client/plasmawindowmanagement.cpp +++ b/src/client/plasmawindowmanagement.cpp @@ -19,6 +19,7 @@ *********************************************************************/ #include "plasmawindowmanagement.h" #include "plasmawindowmodel.h" +#include "plasmavirtualdesktop.h" #include "event_queue.h" #include "output.h" #include "surface.h" @@ -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,20 @@ return d->geometry; } +void PlasmaWindow::requestEnterVirtualDesktop(const QString &id) +{ + org_kde_plasma_window_request_enter_virtual_desktop(d->window, id.toUtf8()); +} + +void PlasmaWindow::requestLeaveVirtualDesktop(const QString &id) +{ + org_kde_plasma_window_request_leave_virtual_desktop(d->window, id.toUtf8()); +} + +QStringList PlasmaWindow::plasmaVirtualDesktops() const +{ + return d->plasmaVirtualDesktops; +} + } } diff --git a/src/client/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-virtual-desktop.xml b/src/client/protocols/plasma-virtual-desktop.xml new file mode 100644 --- /dev/null +++ b/src/client/protocols/plasma-virtual-desktop.xml @@ -0,0 +1,102 @@ + + + . + ]]> + + + + + Given the id of a particular virtual desktop, get the corresponding org_kde_plasma_virtual_desktop which represents only the desktop with that id; + + + + + + + + + + + + + + + + + + + + This event is sent after all other properties has been + sent after binding to the desktop manager object and after any + other property changes done after that. This allows + changes to the org_kde_plasma_virtual_desktop_management properties to be seen as + atomic, even if they happen via multiple events. + + + + + + + + + Request the server to set the status of this desktop to active: The server is free to consent or deny the request. This will be the new "current" virtual desktop of the system. + + + + + + The format of the id is decided by the compositor implementation. A desktop id univocally identifies a virtual desktop and must be guaranteed to never exist two desktops with the same id. The format of the string id is up to the server implementation. + + + + + + + + + + + The desktop will be the new "current" desktop of the system. The server may support either one virtual desktop active at a time, or other combinations such as one virtual desktop active per screen. + Windows associated to this virtual desktop will be shown. + + + + + + Windows that were associated only to this desktop will be hidden. + + + + + + This event is sent after all other properties has been + sent after binding to the desktop object and after any + other property changes done after that. This allows + changes to the org_kde_plasma_virtual_desktop properties to be seen as + atomic, even if they happen via multiple events. + + + + + + This virtual desktop has just been removed by the server: + All windows will lose the association to this desktop. + + + + + diff --git a/src/client/protocols/plasma-window-management.xml b/src/client/protocols/plasma-window-management.xml --- a/src/client/protocols/plasma-window-management.xml +++ b/src/client/protocols/plasma-window-management.xml @@ -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,38 @@ + + + + + + Make the window enter a virtual desktop. A window can enter more + than one virtual desktop. + + + + + + + Make the window exit a virtual desktop. If it exits all desktops it will be considered on all of them. + + + + + + + This event will be sent when the window has entered a new virtual desktop. The window can be on more than one desktop, or none: then is considered on all of them. + + + + + + + This event will be sent when the window left a virtual desktop. If the window leaves all desktops, it can be considered on all. + 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; 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 PlasmaVirtualDesktopManagement; 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 + PlasmaVirtualDesktopManagement, ///< Refers to org_kde_plasma_virtual_desktop_management 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 with @p name and @p version. + * If the @p name does not exist or is not for the Plasma Virtual desktop interface, + * @c null will be returned. + * + * Prefer using createPlasmaShell instead. + * @see createPlasmaShell + * @since 5.46 + **/ + org_kde_plasma_virtual_desktop_management *bindPlasmaVirtualDesktopManagement(uint32_t name, uint32_t version) const; + /** * Binds the org_kde_plasma_window_management with @p name and @p version. * If the @p name does not exist or is not for the Plasma window management interface, * @c null will be returned. * * 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 PlasmaVirtualDesktopManagement and sets it up to manage the interface identified by + * @p name and @p version. + * + * Note: in case @p name is invalid or isn't for the org_kde_plasma_virtual_desktop_management interface, + * the returned VirtualDesktop will not be valid. Therefore it's recommended to call + * isValid on the created instance. + * + * @param name The name of the org_kde_plasma_virtual_desktop_management interface to bind + * @param version The version or the org_kde_plasma_virtual_desktop_management interface to use + * @param parent The parent for PlasmaShell + * + * @returns The created PlasmaShell. + * @since 5.46 + **/ + PlasmaVirtualDesktopManagement *createPlasmaVirtualDesktopManagement(quint32 name, quint32 version, QObject *parent = nullptr); + /** * Creates a PlasmaWindowManagement and sets it up to manage the interface identified by * @p name and @p version. * @@ -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 interface gets announced. + * @param name The name for the announced interface + * @param version The maximum supported version of the announced interface + * @since 5.46 + **/ + void plasmaVirtualDesktopManagementAnnounced(quint32 name, quint32 version); + /** * Emitted whenever a org_kde_plasma_window_management interface gets announced. * @param name The name for the announced interface * @param version The maximum supported version of the announced interface @@ -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 interface gets removed. + * @param name The name for the removed interface + * @since 5.46 + **/ + void plasmaVirtualDesktopManagementRemoved(quint32 name); + /** * Emitted whenever a org_kde_plasma_window_management interface gets removed. * @param name The name for the removed interface * @since 5.4 @@ -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.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::PlasmaVirtualDesktopManagement, { + 1, + QByteArrayLiteral("org_kde_plasma_virtual_desktop_management"), + &org_kde_plasma_virtual_desktop_management_interface, + &Registry::plasmaVirtualDesktopManagementAnnounced, + &Registry::plasmaVirtualDesktopManagementRemoved + }}, {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(PlasmaVirtualDesktopManagement, org_kde_plasma_virtual_desktop_management) BIND(PlasmaWindowManagement, org_kde_plasma_window_management) BIND(Idle, org_kde_kwin_idle) BIND(RemoteAccessManager, org_kde_kwin_remote_access_manager) @@ -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(PlasmaVirtualDesktopManagement) 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.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.xml + BASENAME plasma-virtual-desktop +) + +ecm_add_wayland_server_protocol(SERVER_LIB_SRCS PROTOCOL ${KWayland_SOURCE_DIR}/src/client/protocols/plasma-window-management.xml BASENAME plasma-window-management ) @@ -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-server-protocol.h + ${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-virtual-desktop-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-window-management-client-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-plasma-window-management-server-protocol.h ${CMAKE_CURRENT_BINARY_DIR}/wayland-qt-surface-extension-client-protocol.h @@ -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.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 PlasmaVirtualDesktopManagementInterface; +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 PlasmaVirtualDesktopManagementInterface in interface @p version. + * + * @returns The created manager object + * @since 5.46 + **/ + PlasmaVirtualDesktopManagementInterface *createPlasmaVirtualDesktopManagement(QObject *parent = nullptr); /** * Gets the ClientConnection for the given @p client. 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.h" +#include "xdgoutput_interface.h" #include #include @@ -457,6 +459,21 @@ return b; } + +PlasmaVirtualDesktopManagementInterface *Display::createPlasmaVirtualDesktopManagement(QObject *parent) +{ + auto b = new PlasmaVirtualDesktopManagementInterface(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.h b/src/server/plasmavirtualdesktop_interface.h new file mode 100644 --- /dev/null +++ b/src/server/plasmavirtualdesktop_interface.h @@ -0,0 +1,151 @@ +/**************************************************************************** +Copyright 2018 Marco Martin + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +****************************************************************************/ +#ifndef KWAYLAND_SERVER_PLASMAVIRTUALDESKTOP_H +#define KWAYLAND_SERVER_PLASMAVIRTUALDESKTOP_H + +#include "global.h" +#include "resource.h" + +#include + +namespace KWayland +{ +namespace Server +{ + +class Display; +class PlasmaVirtualDesktopInterface; + +/** + * @short Wrapper for the org_kde_plasma_virtual_desktop_management interface. + * + * This class provides a convenient wrapper for the org_kde_plasma_virtual_desktop_management interface. + * @since 5.46 + */ +class KWAYLANDSERVER_EXPORT PlasmaVirtualDesktopManagementInterface : public Global +{ + Q_OBJECT +public: + virtual ~PlasmaVirtualDesktopManagementInterface(); + + /** + * Sets a new layout for this desktop grid. + */ + void setLayout(quint32 rows, quint32 columns); + + /** + * @returns A desktop identified uniquely by this id. + * If not found, nullptr will be returned. + * @see createDesktop + */ + PlasmaVirtualDesktopInterface *desktop(const QString &id); + + /** + * @returns A desktop identified uniquely by this id, if not found + * a new desktop will be created for this id. + */ + PlasmaVirtualDesktopInterface *createDesktop(const QString &id); + + /** + * Removed and destroys the desktop identified by id, if present + */ + void removeDesktop(const QString &id); + + /** + * @returns All tghe desktops present. + */ + QList desktops() const; + + /** + * Inform the clients that all the properties have been sent, and + * their client-side representation is complete. + */ + void sendDone(); + + /** + * FIXME: RFC this assumes there is only one desktop active at once, may need to be removed if we ever want to support per-screen desktops, or, a second string param could be added as a context for active: that could be the name of an output, or whatever else needed + * Sets the desktop identified by id to be the active one. + * active desktops are mutually exclusive + */ + void setActiveDesktop(const QString &id); + +Q_SIGNALS: + void desktopActivated(const QString &id); + +private: + explicit PlasmaVirtualDesktopManagementInterface(Display *display, QObject *parent = nullptr); + friend class Display; + class Private; + Private *d_func() const; +}; + +class KWAYLANDSERVER_EXPORT PlasmaVirtualDesktopInterface : public QObject +{ + Q_OBJECT +public: + virtual ~PlasmaVirtualDesktopInterface(); + + /** + * @returns the unique id for this desktop. + * ids are set at creation time by PlasmaVirtualDesktopManagementInterface::createDesktop + * and can't be changed at runtime. + */ + QString id() const; + + /** + * Sets a new name for this desktop + */ + void setName(const QString &name); + + /** + * @returns the name for this desktop + */ + QString name() const; + + /** + * @returns true if this desktop is active. Only one at a time will be. + */ + bool active() const; + + /** + * Inform the clients that all the properties have been sent, and + * their client-side representation is complete. + */ + void sendDone(); + +Q_SIGNALS: + /** + * Emitted when the client asked to activate this desktop: + * it's the decision of the server whether to perform the activation or not. + */ + void activateRequested(); + +private: + explicit PlasmaVirtualDesktopInterface(PlasmaVirtualDesktopManagementInterface *parent); + friend class PlasmaVirtualDesktopManagementInterface; + + class Private; + const QScopedPointer d; +}; + +} +} + +#endif diff --git a/src/server/plasmavirtualdesktop_interface.cpp b/src/server/plasmavirtualdesktop_interface.cpp new file mode 100644 --- /dev/null +++ b/src/server/plasmavirtualdesktop_interface.cpp @@ -0,0 +1,406 @@ +/**************************************************************************** +Copyright 2018 Marco Martin + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) version 3, or any +later version accepted by the membership of KDE e.V. (or its +successor approved by the membership of KDE e.V.), which shall +act as a proxy defined in Section 6 of version 3 of the license. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library. If not, see . +****************************************************************************/ +#include "plasmavirtualdesktop_interface.h" +#include "display.h" +#include "global_p.h" +#include "resource_p.h" + +#include +#include + +#include +#include + +namespace KWayland +{ +namespace Server +{ + +class Q_DECL_HIDDEN PlasmaVirtualDesktopInterface::Private +{ +public: + Private(PlasmaVirtualDesktopInterface *q, PlasmaVirtualDesktopManagementInterface *c); + ~Private(); + void createResource(wl_resource *parent, quint32 serial); + + PlasmaVirtualDesktopInterface *q; + PlasmaVirtualDesktopManagementInterface *vdm; + + QVector resources; + QString id; + QString name; + bool active = false; + +private: + static void unbind(wl_resource *resource); + static void requestActivateCallback(wl_client *client, wl_resource *resource); + + static Private *cast(wl_resource *resource) { + return reinterpret_cast(wl_resource_get_user_data(resource)); + } + + wl_listener listener; + static const struct org_kde_plasma_virtual_desktop_interface s_interface; +}; + + +class Q_DECL_HIDDEN PlasmaVirtualDesktopManagementInterface::Private : public Global::Private +{ +public: + Private(PlasmaVirtualDesktopManagementInterface *q, Display *d); + + QVector resources; + QList desktops; + quint32 rows = 0; + quint32 columns = 0; + + inline QList::const_iterator constFindDesktop(const QString &id); + inline QList::iterator findDesktop(const QString &id); +private: + void bind(wl_client *client, uint32_t version, uint32_t id) override; + + static void unbind(wl_resource *resource); + static Private *cast(wl_resource *r) { + return reinterpret_cast(wl_resource_get_user_data(r)); + } + + static void getVirtualDesktopCallback(wl_client *client, wl_resource *resource, uint32_t serial, const char *id); + + PlasmaVirtualDesktopManagementInterface *q; + + static const struct org_kde_plasma_virtual_desktop_management_interface s_interface; + static const quint32 s_version; +}; + +const quint32 PlasmaVirtualDesktopManagementInterface::Private::s_version = 1; + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct org_kde_plasma_virtual_desktop_management_interface PlasmaVirtualDesktopManagementInterface::Private::s_interface = { + getVirtualDesktopCallback +}; +#endif + +inline QList::const_iterator PlasmaVirtualDesktopManagementInterface::Private::constFindDesktop(const QString &id) +{ + return std::find_if( desktops.constBegin(), + desktops.constEnd(), + [id]( const PlasmaVirtualDesktopInterface *desk ){ return desk->id() == id; } ); +} + +inline QList::iterator PlasmaVirtualDesktopManagementInterface::Private::findDesktop(const QString &id) +{ + return std::find_if( desktops.begin(), + desktops.end(), + [id]( const PlasmaVirtualDesktopInterface *desk ){ return desk->id() == id; } ); +} + +void PlasmaVirtualDesktopManagementInterface::Private::getVirtualDesktopCallback(wl_client *client, wl_resource *resource, uint32_t serial, const char *id) +{ + Q_UNUSED(client) + auto s = cast(resource); + + auto i = s->constFindDesktop(QString::fromUtf8(id)); + if (i == s->desktops.constEnd()) { + return; + } + + (*i)->d->createResource(resource, serial); +} + +PlasmaVirtualDesktopManagementInterface::Private::Private(PlasmaVirtualDesktopManagementInterface *q, Display *d) + : Global::Private(d, &org_kde_plasma_virtual_desktop_management_interface, s_version) + , q(q) +{ +} + +void PlasmaVirtualDesktopManagementInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) +{ + auto c = display->getConnection(client); + wl_resource *resource = c->createResource(&org_kde_plasma_virtual_desktop_management_interface, qMin(version, s_version), id); + + if (!resource) { + wl_client_post_no_memory(client); + return; + } + resources << resource; + + wl_resource_set_implementation(resource, &s_interface, this, unbind); + + quint32 i = 0; + for (auto it = desktops.constBegin(); it != desktops.constEnd(); ++it) { + org_kde_plasma_virtual_desktop_management_send_desktop_added(resource, (*it)->id().toUtf8().constData(), i++); + } +} + +void PlasmaVirtualDesktopManagementInterface::Private::unbind(wl_resource *resource) +{ + auto dm = reinterpret_cast(wl_resource_get_user_data(resource)); + dm->resources.removeAll(resource); +} + +PlasmaVirtualDesktopManagementInterface::PlasmaVirtualDesktopManagementInterface(Display *display, QObject *parent) + : Global(new Private(this, display), parent) +{ +} + +PlasmaVirtualDesktopManagementInterface::~PlasmaVirtualDesktopManagementInterface() +{} + +PlasmaVirtualDesktopManagementInterface::Private *PlasmaVirtualDesktopManagementInterface::d_func() const +{ + return reinterpret_cast(d.data()); +} + +PlasmaVirtualDesktopInterface *PlasmaVirtualDesktopManagementInterface::desktop(const QString &id) +{ + Q_D(); + auto i = d->constFindDesktop(id); + if (i != d->desktops.constEnd()) { + return *i; + } + return nullptr; +} + +PlasmaVirtualDesktopInterface *PlasmaVirtualDesktopManagementInterface::createDesktop(const QString &id) +{ + Q_D(); + auto i = d->constFindDesktop(id); + if (i != d->desktops.constEnd()) { + return *i; + } + + PlasmaVirtualDesktopInterface *desktop = new PlasmaVirtualDesktopInterface(this); + desktop->d->id = id; + for (auto it = desktop->d->resources.constBegin(); it != desktop->d->resources.constEnd(); ++it) { + org_kde_plasma_virtual_desktop_send_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 << desktop; + //NOTE: this in case the desktop has been deleted but not trough removedesktop + connect(desktop, &QObject::destroyed, this, + [this, id] { + Q_D(); + auto i = d->findDesktop(id); + if (i != d->desktops.end()) { + for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { + org_kde_plasma_virtual_desktop_management_send_desktop_removed(*it, id.toUtf8().constData()); + } + //Activate another desktop + if (d->desktops.length() > 0 && (*i)->active()) { + if (i == d->desktops.begin()) { + setActiveDesktop((*(i + 1))->id()); + } else { + setActiveDesktop((*(i - 1))->id()); + } + } + d->desktops.erase(i); + } + } + ); + + for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { + org_kde_plasma_virtual_desktop_management_send_desktop_added(*it, id.toUtf8().constData(), d->desktops.length()-1); + } + + return desktop; +} + +void PlasmaVirtualDesktopManagementInterface::removeDesktop(const QString &id) +{ + Q_D(); + auto deskIt = d->findDesktop(id); + if (deskIt == d->desktops.end()) { + return; + } + + for (auto it = (*deskIt)->d->resources.constBegin(); it != (*deskIt)->d->resources.constEnd(); ++it) { + org_kde_plasma_virtual_desktop_send_removed(*it); + } + + for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { + org_kde_plasma_virtual_desktop_management_send_desktop_removed(*it, id.toUtf8().constData()); + } + + //Activate another desktop + if (d->desktops.length() > 0 && (*deskIt)->active()) { + if (deskIt == d->desktops.begin()) { + setActiveDesktop((*(deskIt + 1))->id()); + } else { + setActiveDesktop((*(deskIt - 1))->id()); + } + } + + d->desktops.erase(deskIt); + (*deskIt)->deleteLater(); +} + +QList PlasmaVirtualDesktopManagementInterface::desktops() const +{ + Q_D(); + return d->desktops; +} + +void PlasmaVirtualDesktopManagementInterface::sendDone() +{ + Q_D(); + for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { + org_kde_plasma_virtual_desktop_management_send_done(*it); + } +} + +void PlasmaVirtualDesktopManagementInterface::setActiveDesktop(const QString &id) +{ + Q_D(); + for (auto it = d->desktops.constBegin(); it != d->desktops.constEnd(); ++it) { + auto desktop = *it; + if (desktop->id() == id) { + desktop->d->active = true; + emit desktopActivated(id); + for (auto it = desktop->d->resources.constBegin(); it != desktop->d->resources.constEnd(); ++it) { + org_kde_plasma_virtual_desktop_send_activated(*it); + } + } else { + if (desktop->d->active) { + desktop->d->active = false; + for (auto it = desktop->d->resources.constBegin(); it != desktop->d->resources.constEnd(); ++it) { + org_kde_plasma_virtual_desktop_send_deactivated(*it); + } + } + } + } +} + + + +//// PlasmaVirtualDesktopInterface + +#ifndef DOXYGEN_SHOULD_SKIP_THIS +const struct org_kde_plasma_virtual_desktop_interface PlasmaVirtualDesktopInterface::Private::s_interface = { + requestActivateCallback +}; +#endif + +void PlasmaVirtualDesktopInterface::Private::requestActivateCallback(wl_client *client, wl_resource *resource) +{ + Q_UNUSED(client) + auto s = cast(resource); + emit s->q->activateRequested(); +} + +PlasmaVirtualDesktopInterface::Private::Private(PlasmaVirtualDesktopInterface *q, PlasmaVirtualDesktopManagementInterface *c) + : q(q), + vdm(c) +{ +} + +PlasmaVirtualDesktopInterface::Private::~Private() +{ + // need to copy, as destroy goes through the destroy listener and modifies the list as we iterate + const auto c = resources; + for (const auto &r : c) { + auto client = wl_resource_get_client(r); + org_kde_plasma_virtual_desktop_send_removed(r); + wl_resource_destroy(r); + wl_client_flush(client); + } +} + +void PlasmaVirtualDesktopInterface::Private::unbind(wl_resource *resource) +{ + Private *p = reinterpret_cast(wl_resource_get_user_data(resource)); + p->resources.removeAll(resource); + if (p->resources.isEmpty()) { + p->q->deleteLater(); + } +} + +void PlasmaVirtualDesktopInterface::Private::createResource(wl_resource *parent, quint32 serial) +{ + ClientConnection *c = vdm->display()->getConnection(wl_resource_get_client(parent)); + wl_resource *resource = c->createResource(&org_kde_plasma_virtual_desktop_interface, wl_resource_get_version(parent), serial); + if (!resource) { + return; + } + wl_resource_set_implementation(resource, &s_interface, this, unbind); + resources << resource; + + org_kde_plasma_virtual_desktop_send_desktop_id(resource, id.toUtf8().constData()); + if (!name.isEmpty()) { + org_kde_plasma_virtual_desktop_send_name(resource, name.toUtf8().constData()); + } + + if (active) { + org_kde_plasma_virtual_desktop_send_activated(resource); + } + + c->flush(); +} + +PlasmaVirtualDesktopInterface::PlasmaVirtualDesktopInterface(PlasmaVirtualDesktopManagementInterface *parent) + : QObject(parent), + d(new Private(this, parent)) +{ +} + +PlasmaVirtualDesktopInterface::~PlasmaVirtualDesktopInterface() +{} + +QString PlasmaVirtualDesktopInterface::id() const +{ + return d->id; +} + +void PlasmaVirtualDesktopInterface::setName(const QString &name) +{ + if (d->name == name) { + return; + } + + d->name = name; + for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { + org_kde_plasma_virtual_desktop_send_name(*it, name.toUtf8().constData()); + } +} + +QString PlasmaVirtualDesktopInterface::name() const +{ + return d->name; +} + +bool PlasmaVirtualDesktopInterface::active() const +{ + return d->active; +} + +void PlasmaVirtualDesktopInterface::sendDone() +{ + for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { + org_kde_plasma_virtual_desktop_send_done(*it); + } +} + +} +} + diff --git a/src/server/plasmawindowmanagement_interface.h b/src/server/plasmawindowmanagement_interface.h --- a/src/server/plasmawindowmanagement_interface.h +++ b/src/server/plasmawindowmanagement_interface.h @@ -37,6 +37,7 @@ class Display; class PlasmaWindowInterface; class SurfaceInterface; +class PlasmaVirtualDesktopManagementInterface; /** * @todo Add documentation @@ -72,6 +73,20 @@ **/ void unmapWindow(PlasmaWindowInterface *window); + /** + * Associate a PlasmaVirtualDesktopManagementInterface to this window management. + * It's necessary to associate one in orderto use the plasma virtual desktop features + * of PlasmaWindowInterface, as a window must know what are the deasktops available + * @since 5.46 + */ + void setPlasmaVirtualDesktopManagementInterface(PlasmaVirtualDesktopManagementInterface *manager); + + /** + * @returns the PlasmaVirtualDesktopManagementInterface associated to this PlasmaWindowManagementInterface + * @since 5.46 + */ + PlasmaVirtualDesktopManagementInterface *plasmaVirtualDesktopManagementInterface() const; + Q_SIGNALS: void requestChangeShowingDesktop(ShowingDesktopState requestedState); @@ -94,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.46 + */ + void addPlasmaVirtualDesktop(const QString &id); + + /** + * Removes a visrtual desktop from a window + * + * @since 5.46 + */ + void removePlasmaVirtualDesktop(const QString &id); + + /** + * The ids of all the desktops currently associated with this window. + * When a desktop is deleted it will be automatically removed from this list + * + * @since 5.46 + */ + QStringList plasmaVirtualDesktops() const; + Q_SIGNALS: void closeRequested(); /** @@ -191,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,25 @@ */ 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.46 + */ + void enterPlasmaVirtualDesktopRequested(const QString &desktop); + + /** + * Emitted when the client wishes to remove this window from a virtual desktop. + * The server will decide whether to consent this request + * @since 5.46 + */ + void leavePlasmaVirtualDesktopRequested(const QString &desktop); + private: friend class PlasmaWindowManagementInterface; explicit PlasmaWindowInterface(PlasmaWindowManagementInterface *wm, QObject *parent); diff --git a/src/server/plasmawindowmanagement_interface.cpp b/src/server/plasmawindowmanagement_interface.cpp --- a/src/server/plasmawindowmanagement_interface.cpp +++ b/src/server/plasmawindowmanagement_interface.cpp @@ -22,6 +22,7 @@ #include "resource_p.h" #include "display.h" #include "surface_interface.h" +#include "plasmavirtualdesktop_interface.h" #include #include @@ -48,6 +49,7 @@ ShowingDesktopState state = ShowingDesktopState::Disabled; QVector resources; QList windows; + QPointer plasmaVirtualDesktopManagementInterface = nullptr; quint32 windowIdCounter = 0; private: @@ -90,6 +92,7 @@ bool unmapped = false; PlasmaWindowInterface *parentWindow = nullptr; QMetaObject::Connection parentWindowDestroyConnection; + QStringList plasmaVirtualDesktops; QRect geometry; private: @@ -103,6 +106,8 @@ static void unsetMinimizedGeometryCallback(wl_client *client, wl_resource *resource, wl_resource *panel); static void destroyCallback(wl_client *client, wl_resource *resource); static void getIconCallback(wl_client *client, wl_resource *resource, int32_t fd); + static void requestEnterVirtualDesktopCallback(wl_client *client, wl_resource *resource, const char *id); + static void requestLeaveVirtualDesktopCallback(wl_client *client, wl_resource *resource, const char *id); static Private *cast(wl_resource *resource) { return reinterpret_cast(wl_resource_get_user_data(resource)); } @@ -119,7 +124,7 @@ static const struct org_kde_plasma_window_interface s_interface; }; -const quint32 PlasmaWindowManagementInterface::Private::s_version = 7; +const quint32 PlasmaWindowManagementInterface::Private::s_version = 9; PlasmaWindowManagementInterface::Private::Private(PlasmaWindowManagementInterface *q, Display *d) : Global::Private(d, &org_kde_plasma_window_management_interface, s_version) @@ -255,6 +260,12 @@ return window; } +QList PlasmaWindowManagementInterface::windows() const +{ + Q_D(); + return d->windows; +} + void PlasmaWindowManagementInterface::unmapWindow(PlasmaWindowInterface *window) { if (!window) { @@ -266,6 +277,21 @@ window->d->unmap(); } +void PlasmaWindowManagementInterface::setPlasmaVirtualDesktopManagementInterface(PlasmaVirtualDesktopManagementInterface *manager) +{ + Q_D(); + if (d->plasmaVirtualDesktopManagementInterface == manager) { + return; + } + d->plasmaVirtualDesktopManagementInterface = manager; +} + +PlasmaVirtualDesktopManagementInterface *PlasmaWindowManagementInterface::plasmaVirtualDesktopManagementInterface() const +{ + Q_D(); + return d->plasmaVirtualDesktopManagementInterface; +} + #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct org_kde_plasma_window_interface PlasmaWindowInterface::Private::s_interface = { setStateCallback, @@ -276,7 +302,9 @@ requestMoveCallback, requestResizeCallback, destroyCallback, - getIconCallback + getIconCallback, + requestEnterVirtualDesktopCallback, + requestLeaveVirtualDesktopCallback }; #endif @@ -425,6 +453,20 @@ ); } +void PlasmaWindowInterface::Private::requestEnterVirtualDesktopCallback(wl_client *client, wl_resource *resource, const char *id) +{ + Q_UNUSED(client) + Private *p = cast(resource); + emit p->q->enterPlasmaVirtualDesktopRequested(QString::fromUtf8(id)); +} + +void PlasmaWindowInterface::Private::requestLeaveVirtualDesktopCallback(wl_client *client, wl_resource *resource, const char *id) +{ + Q_UNUSED(client) + Private *p = cast(resource); + emit p->q->leavePlasmaVirtualDesktopRequested(QString::fromUtf8(id)); +} + void PlasmaWindowInterface::Private::setTitle(const QString &title) { if (m_title == title) { @@ -602,6 +644,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 +726,12 @@ d->setTitle(title); } +#ifndef KWAYLANDSERVER_NO_DEPRECATED void PlasmaWindowInterface::setVirtualDesktop(quint32 desktop) { d->setVirtualDesktop(desktop); } +#endif void PlasmaWindowInterface::unmap() { @@ -728,7 +775,33 @@ void PlasmaWindowInterface::setOnAllDesktops(bool set) { + //the deprecated vd management d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ON_ALL_DESKTOPS, set); + + if (!d->wm->plasmaVirtualDesktopManagementInterface()) { + return; + } + + //the current vd management + if (set) { + //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 { + //enters the desktops which are active (usually only one but not a given) + for (auto desk : d->wm->plasmaVirtualDesktopManagementInterface()->desktops()) { + if (desk->active()) { + 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 +834,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 +851,58 @@ d->setIcon(icon); } +void PlasmaWindowInterface::addPlasmaVirtualDesktop(const QString &id) +{ + //don't add a desktop we're not sure it exists + if (!d->wm->plasmaVirtualDesktopManagementInterface() || d->plasmaVirtualDesktops.contains(id)) { + return; + } + + PlasmaVirtualDesktopInterface *desktop = d->wm->plasmaVirtualDesktopManagementInterface()->desktop(id); + + if (!desktop) { + return; + } + + //full? lets set it on all desktops, the plasmaVirtualDesktops list will get empty, which means it's on all desktops + if (d->wm->plasmaVirtualDesktopManagementInterface()->desktops().count() == d->plasmaVirtualDesktops.count() + 1) { + setOnAllDesktops(true); + return; + } + + d->plasmaVirtualDesktops << id; + + //if the desktop dies, remove it from or list + connect(desktop, &QObject::destroyed, + this, [this, id](){removePlasmaVirtualDesktop(id);}); + + for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { + org_kde_plasma_window_send_virtual_desktop_entered(*it, id.toUtf8().constData()); + } +} + +void PlasmaWindowInterface::removePlasmaVirtualDesktop(const QString &id) +{ + if (!d->plasmaVirtualDesktops.contains(id)) { + return; + } + + d->plasmaVirtualDesktops.removeAll(id); + for (auto it = d->resources.constBegin(); it != d->resources.constEnd(); ++it) { + org_kde_plasma_window_send_virtual_desktop_left(*it, id.toUtf8().constData()); + } + + //we went on all desktops + if (d->plasmaVirtualDesktops.isEmpty()) { + setOnAllDesktops(true); + } +} + +QStringList PlasmaWindowInterface::plasmaVirtualDesktops() const +{ + return d->plasmaVirtualDesktops; +} + void PlasmaWindowInterface::setShadeable(bool set) { d->setState(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADEABLE, set); 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();