diff --git a/autotests/client/test_plasma_window_model.cpp b/autotests/client/test_plasma_window_model.cpp index 640f61c..c3809a0 100644 --- a/autotests/client/test_plasma_window_model.cpp +++ b/autotests/client/test_plasma_window_model.cpp @@ -1,867 +1,860 @@ /******************************************************************** Copyright 2016 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ // Qt #include // client #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/registry.h" #include "../../src/client/plasmawindowmanagement.h" #include "../../src/client/plasmawindowmodel.h" // server #include "../../src/server/display.h" #include "../../src/server/plasmawindowmanagement_interface.h" #include using namespace KWayland::Client; using namespace KWayland::Server; Q_DECLARE_METATYPE(Qt::MouseButton) typedef void (KWayland::Client::PlasmaWindow::*ClientWindowSignal)(); Q_DECLARE_METATYPE(ClientWindowSignal) typedef void (KWayland::Server::PlasmaWindowInterface::*ServerWindowBoolSetter)(bool); Q_DECLARE_METATYPE(ServerWindowBoolSetter) typedef void (KWayland::Server::PlasmaWindowInterface::*ServerWindowStringSetter)(const QString&); Q_DECLARE_METATYPE(ServerWindowStringSetter) typedef void (KWayland::Server::PlasmaWindowInterface::*ServerWindowQuint32Setter)(quint32); Q_DECLARE_METATYPE(ServerWindowQuint32Setter) typedef void (KWayland::Server::PlasmaWindowInterface::*ServerWindowVoidSetter)(); Q_DECLARE_METATYPE(ServerWindowVoidSetter) class PlasmaWindowModelTest : public QObject { Q_OBJECT private Q_SLOTS: void init(); void cleanup(); void testRoleNames_data(); void testRoleNames(); void testAddRemoveRows(); void testDefaultData_data(); void testDefaultData(); void testIsActive(); void testIsFullscreenable(); void testIsFullscreen(); void testIsMaximizable(); void testIsMaximized(); void testIsMinimizable(); void testIsMinimized(); void testIsKeepAbove(); void testIsKeepBelow(); void testIsOnAllDesktops(); void testIsDemandingAttention(); void testSkipTaskbar(); void testIsShadeable(); void testIsShaded(); void testIsMovable(); void testIsResizable(); void testIsVirtualDesktopChangeable(); void testIsCloseable(); void testGeometry(); void testTitle(); void testAppId(); void testPid(); void testVirtualDesktop(); // TODO icon: can we ensure a theme is installed on CI? void testRequests(); // TODO: minimized geometry // TODO: model reset void testCreateWithUnmappedWindow(); void testChangeWindowAfterModelDestroy_data(); void testChangeWindowAfterModelDestroy(); void testCreateWindowAfterModelDestroy(); private: bool testBooleanData(PlasmaWindowModel::AdditionalRoles role, void (PlasmaWindowInterface::*function)(bool)); Display *m_display = nullptr; PlasmaWindowManagementInterface *m_pwInterface = nullptr; PlasmaWindowManagement *m_pw = nullptr; ConnectionThread *m_connection = nullptr; QThread *m_thread = nullptr; EventQueue *m_queue = nullptr; }; static const QString s_socketName = QStringLiteral("kwayland-test-fake-input-0"); void PlasmaWindowModelTest::init() { delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); m_display->createShm(); m_pwInterface = m_display->createPlasmaWindowManagement(); m_pwInterface->create(); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new EventQueue(this); m_queue->setup(m_connection); Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); registry.setEventQueue(m_queue); registry.create(m_connection); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(interfacesAnnouncedSpy.wait()); m_pw = registry.createPlasmaWindowManagement(registry.interface(Registry::Interface::PlasmaWindowManagement).name, registry.interface(Registry::Interface::PlasmaWindowManagement).version, this); QVERIFY(m_pw->isValid()); } void PlasmaWindowModelTest::cleanup() { #define CLEANUP(variable) \ if (variable) { \ delete variable; \ variable = nullptr; \ } CLEANUP(m_pw) CLEANUP(m_queue) if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } CLEANUP(m_pwInterface) CLEANUP(m_display) #undef CLEANUP } bool PlasmaWindowModelTest::testBooleanData(PlasmaWindowModel::AdditionalRoles role, void (PlasmaWindowInterface::*function)(bool)) { #define VERIFY(statement) \ if (!QTest::qVerify((statement), #statement, "", __FILE__, __LINE__))\ return false; #define COMPARE(actual, expected) \ if (!QTest::qCompare(actual, expected, #actual, #expected, __FILE__, __LINE__))\ return false; auto model = m_pw->createWindowModel(); VERIFY(model); QSignalSpy rowInsertedSpy(model, &PlasmaWindowModel::rowsInserted); VERIFY(rowInsertedSpy.isValid()); auto w = m_pwInterface->createWindow(m_pwInterface); VERIFY(w); VERIFY(rowInsertedSpy.wait()); m_connection->flush(); m_display->dispatchEvents(); QSignalSpy dataChangedSpy(model, &PlasmaWindowModel::dataChanged); VERIFY(dataChangedSpy.isValid()); const QModelIndex index = model->index(0); COMPARE(model->data(index, role).toBool(), false); (w->*(function))(true); VERIFY(dataChangedSpy.wait()); COMPARE(dataChangedSpy.count(), 1); COMPARE(dataChangedSpy.last().first().toModelIndex(), index); COMPARE(dataChangedSpy.last().last().value>(), QVector{int(role)}); COMPARE(model->data(index, role).toBool(), true); (w->*(function))(false); VERIFY(dataChangedSpy.wait()); COMPARE(dataChangedSpy.count(), 2); COMPARE(dataChangedSpy.last().first().toModelIndex(), index); COMPARE(dataChangedSpy.last().last().value>(), QVector{int(role)}); COMPARE(model->data(index, role).toBool(), false); #undef COMPARE #undef VERIFY return true; } void PlasmaWindowModelTest::testRoleNames_data() { QTest::addColumn("role"); QTest::addColumn("name"); QTest::newRow("display") << int(Qt::DisplayRole) << QByteArrayLiteral("DisplayRole"); QTest::newRow("decoration") << int(Qt::DecorationRole) << QByteArrayLiteral("DecorationRole"); QTest::newRow("AppId") << int(PlasmaWindowModel::AppId) << QByteArrayLiteral("AppId"); QTest::newRow("Pid") << int(PlasmaWindowModel::Pid) << QByteArrayLiteral("Pid"); QTest::newRow("IsActive") << int(PlasmaWindowModel::IsActive) << QByteArrayLiteral("IsActive"); QTest::newRow("IsFullscreenable") << int(PlasmaWindowModel::IsFullscreenable) << QByteArrayLiteral("IsFullscreenable"); QTest::newRow("IsFullscreen") << int(PlasmaWindowModel::IsFullscreen) << QByteArrayLiteral("IsFullscreen"); QTest::newRow("IsMaximizable") << int(PlasmaWindowModel::IsMaximizable) << QByteArrayLiteral("IsMaximizable"); QTest::newRow("IsMaximized") << int(PlasmaWindowModel::IsMaximized) << QByteArrayLiteral("IsMaximized"); QTest::newRow("IsMinimizable") << int(PlasmaWindowModel::IsMinimizable) << QByteArrayLiteral("IsMinimizable"); QTest::newRow("IsMinimized") << int(PlasmaWindowModel::IsMinimized) << QByteArrayLiteral("IsMinimized"); QTest::newRow("IsKeepAbove") << int(PlasmaWindowModel::IsKeepAbove) << QByteArrayLiteral("IsKeepAbove"); QTest::newRow("IsKeepBelow") << int(PlasmaWindowModel::IsKeepBelow) << QByteArrayLiteral("IsKeepBelow"); QTest::newRow("VirtualDesktop") << int(PlasmaWindowModel::VirtualDesktop) << QByteArrayLiteral("VirtualDesktop"); QTest::newRow("IsOnAllDesktops") << int(PlasmaWindowModel::IsOnAllDesktops) << QByteArrayLiteral("IsOnAllDesktops"); QTest::newRow("IsDemandingAttention") << int(PlasmaWindowModel::IsDemandingAttention) << QByteArrayLiteral("IsDemandingAttention"); QTest::newRow("SkipTaskbar") << int(PlasmaWindowModel::SkipTaskbar) << QByteArrayLiteral("SkipTaskbar"); QTest::newRow("IsShadeable") << int(PlasmaWindowModel::IsShadeable) << QByteArrayLiteral("IsShadeable"); QTest::newRow("IsShaded") << int(PlasmaWindowModel::IsShaded) << QByteArrayLiteral("IsShaded"); QTest::newRow("IsMovable") << int(PlasmaWindowModel::IsMovable) << QByteArrayLiteral("IsMovable"); QTest::newRow("IsResizable") << int(PlasmaWindowModel::IsResizable) << QByteArrayLiteral("IsResizable"); QTest::newRow("IsVirtualDesktopChangeable") << int(PlasmaWindowModel::IsVirtualDesktopChangeable) << QByteArrayLiteral("IsVirtualDesktopChangeable"); QTest::newRow("IsCloseable") << int(PlasmaWindowModel::IsCloseable) << QByteArrayLiteral("IsCloseable"); QTest::newRow("Geometry") << int(PlasmaWindowModel::Geometry) << QByteArrayLiteral("Geometry"); } void PlasmaWindowModelTest::testRoleNames() { // just verifies that all role names are available auto model = m_pw->createWindowModel(); QVERIFY(model); const auto roles = model->roleNames(); QFETCH(int, role); auto it = roles.find(role); QVERIFY(it != roles.end()); QTEST(it.value(), "name"); } void PlasmaWindowModelTest::testAddRemoveRows() { // this test verifies that adding/removing rows to the Model works auto model = m_pw->createWindowModel(); QVERIFY(model); QCOMPARE(model->rowCount(), 0); QVERIFY(!model->index(0).isValid()); // now let's add a row QSignalSpy rowInsertedSpy(model, &PlasmaWindowModel::rowsInserted); QVERIFY(rowInsertedSpy.isValid()); // this happens by creating a PlasmaWindow on server side auto w = m_pwInterface->createWindow(m_pwInterface); QVERIFY(w); QVERIFY(rowInsertedSpy.wait()); QCOMPARE(rowInsertedSpy.count(), 1); QVERIFY(!rowInsertedSpy.first().at(0).toModelIndex().isValid()); QCOMPARE(rowInsertedSpy.first().at(1).toInt(), 0); QCOMPARE(rowInsertedSpy.first().at(2).toInt(), 0); // the model should have a row now QCOMPARE(model->rowCount(), 1); QVERIFY(model->index(0).isValid()); // that index doesn't have children QCOMPARE(model->rowCount(model->index(0)), 0); // process events in order to ensure that the resource is created on server side before we unmap the window QCoreApplication::instance()->processEvents(QEventLoop::WaitForMoreEvents); // now let's remove that again QSignalSpy rowRemovedSpy(model, &PlasmaWindowModel::rowsRemoved); QVERIFY(rowRemovedSpy.isValid()); w->unmap(); QVERIFY(rowRemovedSpy.wait()); QCOMPARE(rowRemovedSpy.count(), 1); QVERIFY(!rowRemovedSpy.first().at(0).toModelIndex().isValid()); QCOMPARE(rowRemovedSpy.first().at(1).toInt(), 0); QCOMPARE(rowRemovedSpy.first().at(2).toInt(), 0); // now the model is empty again QCOMPARE(model->rowCount(), 0); QVERIFY(!model->index(0).isValid()); QSignalSpy wDestroyedSpy(w, &QObject::destroyed); QVERIFY(wDestroyedSpy.isValid()); QVERIFY(wDestroyedSpy.wait()); } void PlasmaWindowModelTest::testDefaultData_data() { QTest::addColumn("role"); QTest::addColumn("value"); QTest::newRow("display") << int(Qt::DisplayRole) << QVariant(QString()); QTest::newRow("decoration") << int(Qt::DecorationRole) << QVariant(QIcon()); QTest::newRow("AppId") << int(PlasmaWindowModel::AppId) << QVariant(QString()); QTest::newRow("IsActive") << int(PlasmaWindowModel::IsActive) << QVariant(false); QTest::newRow("IsFullscreenable") << int(PlasmaWindowModel::IsFullscreenable) << QVariant(false); QTest::newRow("IsFullscreen") << int(PlasmaWindowModel::IsFullscreen) << QVariant(false); QTest::newRow("IsMaximizable") << int(PlasmaWindowModel::IsMaximizable) << QVariant(false); QTest::newRow("IsMaximized") << int(PlasmaWindowModel::IsMaximized) << QVariant(false); QTest::newRow("IsMinimizable") << int(PlasmaWindowModel::IsMinimizable) << QVariant(false); QTest::newRow("IsMinimized") << int(PlasmaWindowModel::IsMinimized) << QVariant(false); QTest::newRow("IsKeepAbove") << int(PlasmaWindowModel::IsKeepAbove) << QVariant(false); QTest::newRow("IsKeepBelow") << int(PlasmaWindowModel::IsKeepBelow) << QVariant(false); QTest::newRow("VirtualDesktop") << int(PlasmaWindowModel::VirtualDesktop) << QVariant(0); QTest::newRow("IsOnAllDesktops") << int(PlasmaWindowModel::IsOnAllDesktops) << QVariant(false); QTest::newRow("IsDemandingAttention") << int(PlasmaWindowModel::IsDemandingAttention) << QVariant(false); QTest::newRow("IsShadeable") << int(PlasmaWindowModel::IsShadeable) << QVariant(false); QTest::newRow("IsShaded") << int(PlasmaWindowModel::IsShaded) << QVariant(false); QTest::newRow("SkipTaskbar") << int(PlasmaWindowModel::SkipTaskbar) << QVariant(false); QTest::newRow("IsMovable") << int(PlasmaWindowModel::IsMovable) << QVariant(false); QTest::newRow("IsResizable") << int(PlasmaWindowModel::IsResizable) << QVariant(false); QTest::newRow("IsVirtualDesktopChangeable") << int(PlasmaWindowModel::IsVirtualDesktopChangeable) << QVariant(false); QTest::newRow("IsCloseable") << int(PlasmaWindowModel::IsCloseable) << QVariant(false); QTest::newRow("Geometry") << int(PlasmaWindowModel::Geometry) << QVariant(QRect()); QTest::newRow("Pid") << int(PlasmaWindowModel::Pid) << QVariant(0); } void PlasmaWindowModelTest::testDefaultData() { // this test validates the default data of a PlasmaWindow without having set any values // first create a model with a window auto model = m_pw->createWindowModel(); QVERIFY(model); QSignalSpy rowInsertedSpy(model, &PlasmaWindowModel::rowsInserted); QVERIFY(rowInsertedSpy.isValid()); auto w = m_pwInterface->createWindow(m_pwInterface); QVERIFY(w); QVERIFY(rowInsertedSpy.wait()); QModelIndex index = model->index(0); QFETCH(int, role); QTEST(model->data(index, role), "value"); } void PlasmaWindowModelTest::testIsActive() { QVERIFY(testBooleanData(PlasmaWindowModel::IsActive, &PlasmaWindowInterface::setActive)); } void PlasmaWindowModelTest::testIsFullscreenable() { QVERIFY(testBooleanData(PlasmaWindowModel::IsFullscreenable, &PlasmaWindowInterface::setFullscreenable)); } void PlasmaWindowModelTest::testIsFullscreen() { QVERIFY(testBooleanData(PlasmaWindowModel::IsFullscreen, &PlasmaWindowInterface::setFullscreen)); } void PlasmaWindowModelTest::testIsMaximizable() { QVERIFY(testBooleanData(PlasmaWindowModel::IsMaximizable, &PlasmaWindowInterface::setMaximizeable)); } void PlasmaWindowModelTest::testIsMaximized() { QVERIFY(testBooleanData(PlasmaWindowModel::IsMaximized, &PlasmaWindowInterface::setMaximized)); } void PlasmaWindowModelTest::testIsMinimizable() { QVERIFY(testBooleanData(PlasmaWindowModel::IsMinimizable, &PlasmaWindowInterface::setMinimizeable)); } void PlasmaWindowModelTest::testIsMinimized() { QVERIFY(testBooleanData(PlasmaWindowModel::IsMinimized, &PlasmaWindowInterface::setMinimized)); } void PlasmaWindowModelTest::testIsKeepAbove() { QVERIFY(testBooleanData(PlasmaWindowModel::IsKeepAbove, &PlasmaWindowInterface::setKeepAbove)); } void PlasmaWindowModelTest::testIsKeepBelow() { QVERIFY(testBooleanData(PlasmaWindowModel::IsKeepBelow, &PlasmaWindowInterface::setKeepBelow)); } void PlasmaWindowModelTest::testIsOnAllDesktops() { QVERIFY(testBooleanData(PlasmaWindowModel::IsOnAllDesktops, &PlasmaWindowInterface::setOnAllDesktops)); } void PlasmaWindowModelTest::testIsDemandingAttention() { QVERIFY(testBooleanData(PlasmaWindowModel::IsDemandingAttention, &PlasmaWindowInterface::setDemandsAttention)); } void PlasmaWindowModelTest::testSkipTaskbar() { QVERIFY(testBooleanData(PlasmaWindowModel::SkipTaskbar, &PlasmaWindowInterface::setSkipTaskbar)); } void PlasmaWindowModelTest::testIsShadeable() { QVERIFY(testBooleanData(PlasmaWindowModel::IsShadeable, &PlasmaWindowInterface::setShadeable)); } void PlasmaWindowModelTest::testIsShaded() { QVERIFY(testBooleanData(PlasmaWindowModel::IsShaded, &PlasmaWindowInterface::setShaded)); } void PlasmaWindowModelTest::testIsMovable() { QVERIFY(testBooleanData(PlasmaWindowModel::IsMovable, &PlasmaWindowInterface::setMovable)); } void PlasmaWindowModelTest::testIsResizable() { QVERIFY(testBooleanData(PlasmaWindowModel::IsResizable, &PlasmaWindowInterface::setResizable)); } void PlasmaWindowModelTest::testIsVirtualDesktopChangeable() { QVERIFY(testBooleanData(PlasmaWindowModel::IsVirtualDesktopChangeable, &PlasmaWindowInterface::setVirtualDesktopChangeable)); } void PlasmaWindowModelTest::testIsCloseable() { QVERIFY(testBooleanData(PlasmaWindowModel::IsCloseable, &PlasmaWindowInterface::setCloseable)); } void PlasmaWindowModelTest::testGeometry() { auto model = m_pw->createWindowModel(); QVERIFY(model); QSignalSpy rowInsertedSpy(model, &PlasmaWindowModel::rowsInserted); QVERIFY(rowInsertedSpy.isValid()); auto w = m_pwInterface->createWindow(m_pwInterface); QVERIFY(w); QVERIFY(rowInsertedSpy.wait()); const QModelIndex index = model->index(0); QCOMPARE(model->data(index, PlasmaWindowModel::Geometry).toRect(), QRect()); QSignalSpy dataChangedSpy(model, &PlasmaWindowModel::dataChanged); QVERIFY(dataChangedSpy.isValid()); const QRect geom(0, 15, 50, 75); w->setGeometry(geom); QVERIFY(dataChangedSpy.wait()); QCOMPARE(dataChangedSpy.count(), 1); QCOMPARE(dataChangedSpy.last().first().toModelIndex(), index); QCOMPARE(dataChangedSpy.last().last().value>(), QVector{int(PlasmaWindowModel::Geometry)}); QCOMPARE(model->data(index, PlasmaWindowModel::Geometry).toRect(), geom); } void PlasmaWindowModelTest::testTitle() { auto model = m_pw->createWindowModel(); QVERIFY(model); QSignalSpy rowInsertedSpy(model, &PlasmaWindowModel::rowsInserted); QVERIFY(rowInsertedSpy.isValid()); auto w = m_pwInterface->createWindow(m_pwInterface); QVERIFY(w); QVERIFY(rowInsertedSpy.wait()); m_connection->flush(); m_display->dispatchEvents(); QSignalSpy dataChangedSpy(model, &PlasmaWindowModel::dataChanged); QVERIFY(dataChangedSpy.isValid()); const QModelIndex index = model->index(0); QCOMPARE(model->data(index, Qt::DisplayRole).toString(), QString()); w->setTitle(QStringLiteral("foo")); QVERIFY(dataChangedSpy.wait()); QCOMPARE(dataChangedSpy.count(), 1); QCOMPARE(dataChangedSpy.last().first().toModelIndex(), index); QCOMPARE(dataChangedSpy.last().last().value>(), QVector{int(Qt::DisplayRole)}); QCOMPARE(model->data(index, Qt::DisplayRole).toString(), QStringLiteral("foo")); } void PlasmaWindowModelTest::testAppId() { auto model = m_pw->createWindowModel(); QVERIFY(model); QSignalSpy rowInsertedSpy(model, &PlasmaWindowModel::rowsInserted); QVERIFY(rowInsertedSpy.isValid()); auto w = m_pwInterface->createWindow(m_pwInterface); QVERIFY(w); QVERIFY(rowInsertedSpy.wait()); m_connection->flush(); m_display->dispatchEvents(); QSignalSpy dataChangedSpy(model, &PlasmaWindowModel::dataChanged); QVERIFY(dataChangedSpy.isValid()); const QModelIndex index = model->index(0); QCOMPARE(model->data(index, PlasmaWindowModel::AppId).toString(), QString()); w->setAppId(QStringLiteral("org.kde.testapp")); QVERIFY(dataChangedSpy.wait()); QCOMPARE(dataChangedSpy.count(), 1); QCOMPARE(dataChangedSpy.last().first().toModelIndex(), index); QCOMPARE(dataChangedSpy.last().last().value>(), QVector{int(PlasmaWindowModel::AppId)}); QCOMPARE(model->data(index, PlasmaWindowModel::AppId).toString(), QStringLiteral("org.kde.testapp")); } void PlasmaWindowModelTest::testPid() { auto model = m_pw->createWindowModel(); QVERIFY(model); QSignalSpy rowInsertedSpy(model, &PlasmaWindowModel::rowsInserted); QVERIFY(rowInsertedSpy.isValid()); auto w = m_pwInterface->createWindow(m_pwInterface); + w->setPid(1337); QVERIFY(w); - QVERIFY(rowInsertedSpy.wait()); m_connection->flush(); m_display->dispatchEvents(); - QSignalSpy dataChangedSpy(model, &PlasmaWindowModel::dataChanged); - QVERIFY(dataChangedSpy.isValid()); + QVERIFY(rowInsertedSpy.wait()); + //pid should be set as soon as the new row appears const QModelIndex index = model->index(0); - QCOMPARE(model->data(index, PlasmaWindowModel::Pid).toInt(), 0); - - w->setPid(1337); - QVERIFY(dataChangedSpy.wait()); - QCOMPARE(dataChangedSpy.count(), 1); - QCOMPARE(dataChangedSpy.last().first().toModelIndex(), index); - QCOMPARE(dataChangedSpy.last().last().value>(), QVector{int(PlasmaWindowModel::Pid)}); QCOMPARE(model->data(index, PlasmaWindowModel::Pid).toInt(), 1337); } void PlasmaWindowModelTest::testVirtualDesktop() { auto model = m_pw->createWindowModel(); QVERIFY(model); QSignalSpy rowInsertedSpy(model, &PlasmaWindowModel::rowsInserted); QVERIFY(rowInsertedSpy.isValid()); auto w = m_pwInterface->createWindow(m_pwInterface); QVERIFY(w); QVERIFY(rowInsertedSpy.wait()); m_connection->flush(); m_display->dispatchEvents(); QSignalSpy dataChangedSpy(model, &PlasmaWindowModel::dataChanged); QVERIFY(dataChangedSpy.isValid()); const QModelIndex index = model->index(0); QCOMPARE(model->data(index, PlasmaWindowModel::VirtualDesktop).toInt(), 0); w->setVirtualDesktop(1); QVERIFY(dataChangedSpy.wait()); QCOMPARE(dataChangedSpy.count(), 1); QCOMPARE(dataChangedSpy.last().first().toModelIndex(), index); QCOMPARE(dataChangedSpy.last().last().value>(), QVector{int(PlasmaWindowModel::VirtualDesktop)}); QCOMPARE(model->data(index, PlasmaWindowModel::VirtualDesktop).toInt(), 1); // setting to same should not trigger w->setVirtualDesktop(1); QVERIFY(!dataChangedSpy.wait(100)); } void PlasmaWindowModelTest::testRequests() { // this test verifies that the various requests are properly passed to the server auto model = m_pw->createWindowModel(); QVERIFY(model); QSignalSpy rowInsertedSpy(model, &PlasmaWindowModel::rowsInserted); QVERIFY(rowInsertedSpy.isValid()); auto w = m_pwInterface->createWindow(m_pwInterface); QVERIFY(w); QVERIFY(rowInsertedSpy.wait()); QSignalSpy activateRequestedSpy(w, &PlasmaWindowInterface::activeRequested); QVERIFY(activateRequestedSpy.isValid()); QSignalSpy closeRequestedSpy(w, &PlasmaWindowInterface::closeRequested); QVERIFY(closeRequestedSpy.isValid()); QSignalSpy moveRequestedSpy(w, &PlasmaWindowInterface::moveRequested); QVERIFY(moveRequestedSpy.isValid()); QSignalSpy resizeRequestedSpy(w, &PlasmaWindowInterface::resizeRequested); QVERIFY(resizeRequestedSpy.isValid()); QSignalSpy virtualDesktopRequestedSpy(w, &PlasmaWindowInterface::virtualDesktopRequested); QVERIFY(virtualDesktopRequestedSpy.isValid()); QSignalSpy minimizedRequestedSpy(w, &PlasmaWindowInterface::minimizedRequested); QVERIFY(minimizedRequestedSpy.isValid()); QSignalSpy maximizeRequestedSpy(w, &PlasmaWindowInterface::maximizedRequested); QVERIFY(maximizeRequestedSpy.isValid()); QSignalSpy shadeRequestedSpy(w, &PlasmaWindowInterface::shadedRequested); QVERIFY(shadeRequestedSpy.isValid()); // first let's use some invalid row numbers model->requestActivate(-1); model->requestClose(-1); model->requestVirtualDesktop(-1, 1); model->requestToggleMinimized(-1); model->requestToggleMaximized(-1); model->requestActivate(1); model->requestClose(1); model->requestMove(1); model->requestResize(1); model->requestVirtualDesktop(1, 1); model->requestToggleMinimized(1); model->requestToggleMaximized(1); model->requestToggleShaded(1); // that should not have triggered any signals QVERIFY(!activateRequestedSpy.wait(100)); QVERIFY(activateRequestedSpy.isEmpty()); QVERIFY(closeRequestedSpy.isEmpty()); QVERIFY(moveRequestedSpy.isEmpty()); QVERIFY(resizeRequestedSpy.isEmpty()); QVERIFY(virtualDesktopRequestedSpy.isEmpty()); QVERIFY(minimizedRequestedSpy.isEmpty()); QVERIFY(maximizeRequestedSpy.isEmpty()); QVERIFY(shadeRequestedSpy.isEmpty()); // now with the proper row // activate model->requestActivate(0); QVERIFY(activateRequestedSpy.wait()); QCOMPARE(activateRequestedSpy.count(), 1); QCOMPARE(activateRequestedSpy.first().first().toBool(), true); QCOMPARE(closeRequestedSpy.count(), 0); QCOMPARE(moveRequestedSpy.count(), 0); QCOMPARE(resizeRequestedSpy.count(), 0); QCOMPARE(virtualDesktopRequestedSpy.count(), 0); QCOMPARE(minimizedRequestedSpy.count(), 0); QCOMPARE(maximizeRequestedSpy.count(), 0); QCOMPARE(shadeRequestedSpy.count(), 0); // close model->requestClose(0); QVERIFY(closeRequestedSpy.wait()); QCOMPARE(activateRequestedSpy.count(), 1); QCOMPARE(closeRequestedSpy.count(), 1); QCOMPARE(moveRequestedSpy.count(), 0); QCOMPARE(resizeRequestedSpy.count(), 0); QCOMPARE(virtualDesktopRequestedSpy.count(), 0); QCOMPARE(minimizedRequestedSpy.count(), 0); QCOMPARE(maximizeRequestedSpy.count(), 0); QCOMPARE(shadeRequestedSpy.count(), 0); // move model->requestMove(0); QVERIFY(moveRequestedSpy.wait()); QCOMPARE(activateRequestedSpy.count(), 1); QCOMPARE(closeRequestedSpy.count(), 1); QCOMPARE(moveRequestedSpy.count(), 1); QCOMPARE(resizeRequestedSpy.count(), 0); QCOMPARE(virtualDesktopRequestedSpy.count(), 0); QCOMPARE(minimizedRequestedSpy.count(), 0); QCOMPARE(maximizeRequestedSpy.count(), 0); QCOMPARE(shadeRequestedSpy.count(), 0); // resize model->requestResize(0); QVERIFY(resizeRequestedSpy.wait()); QCOMPARE(activateRequestedSpy.count(), 1); QCOMPARE(closeRequestedSpy.count(), 1); QCOMPARE(moveRequestedSpy.count(), 1); QCOMPARE(resizeRequestedSpy.count(), 1); QCOMPARE(virtualDesktopRequestedSpy.count(), 0); QCOMPARE(minimizedRequestedSpy.count(), 0); QCOMPARE(maximizeRequestedSpy.count(), 0); QCOMPARE(shadeRequestedSpy.count(), 0); // virtual desktop model->requestVirtualDesktop(0, 1); QVERIFY(virtualDesktopRequestedSpy.wait()); QCOMPARE(virtualDesktopRequestedSpy.count(), 1); QCOMPARE(virtualDesktopRequestedSpy.first().first().toUInt(), 1u); QCOMPARE(activateRequestedSpy.count(), 1); QCOMPARE(closeRequestedSpy.count(), 1); QCOMPARE(moveRequestedSpy.count(), 1); QCOMPARE(resizeRequestedSpy.count(), 1); QCOMPARE(minimizedRequestedSpy.count(), 0); QCOMPARE(maximizeRequestedSpy.count(), 0); QCOMPARE(shadeRequestedSpy.count(), 0); // minimize model->requestToggleMinimized(0); QVERIFY(minimizedRequestedSpy.wait()); QCOMPARE(minimizedRequestedSpy.count(), 1); QCOMPARE(minimizedRequestedSpy.first().first().toBool(), true); QCOMPARE(activateRequestedSpy.count(), 1); QCOMPARE(closeRequestedSpy.count(), 1); QCOMPARE(moveRequestedSpy.count(), 1); QCOMPARE(resizeRequestedSpy.count(), 1); QCOMPARE(virtualDesktopRequestedSpy.count(), 1); QCOMPARE(maximizeRequestedSpy.count(), 0); QCOMPARE(shadeRequestedSpy.count(), 0); // maximize model->requestToggleMaximized(0); QVERIFY(maximizeRequestedSpy.wait()); QCOMPARE(maximizeRequestedSpy.count(), 1); QCOMPARE(maximizeRequestedSpy.first().first().toBool(), true); QCOMPARE(activateRequestedSpy.count(), 1); QCOMPARE(closeRequestedSpy.count(), 1); QCOMPARE(moveRequestedSpy.count(), 1); QCOMPARE(virtualDesktopRequestedSpy.count(), 1); QCOMPARE(minimizedRequestedSpy.count(), 1); QCOMPARE(shadeRequestedSpy.count(), 0); // shade model->requestToggleShaded(0); QVERIFY(shadeRequestedSpy.wait()); QCOMPARE(shadeRequestedSpy.count(), 1); QCOMPARE(shadeRequestedSpy.first().first().toBool(), true); QCOMPARE(activateRequestedSpy.count(), 1); QCOMPARE(closeRequestedSpy.count(), 1); QCOMPARE(moveRequestedSpy.count(), 1); QCOMPARE(resizeRequestedSpy.count(), 1); QCOMPARE(virtualDesktopRequestedSpy.count(), 1); QCOMPARE(minimizedRequestedSpy.count(), 1); QCOMPARE(maximizeRequestedSpy.count(), 1); // the toggles can also support a different state QSignalSpy dataChangedSpy(model, &PlasmaWindowModel::dataChanged); QVERIFY(dataChangedSpy.isValid()); // minimize w->setMinimized(true); QVERIFY(dataChangedSpy.wait()); model->requestToggleMinimized(0); QVERIFY(minimizedRequestedSpy.wait()); QCOMPARE(minimizedRequestedSpy.count(), 2); QCOMPARE(minimizedRequestedSpy.last().first().toBool(), false); // maximized w->setMaximized(true); QVERIFY(dataChangedSpy.wait()); model->requestToggleMaximized(0); QVERIFY(maximizeRequestedSpy.wait()); QCOMPARE(maximizeRequestedSpy.count(), 2); QCOMPARE(maximizeRequestedSpy.last().first().toBool(), false); // shaded w->setShaded(true); QVERIFY(dataChangedSpy.wait()); model->requestToggleShaded(0); QVERIFY(shadeRequestedSpy.wait()); QCOMPARE(shadeRequestedSpy.count(), 2); QCOMPARE(shadeRequestedSpy.last().first().toBool(), false); } void PlasmaWindowModelTest::testCreateWithUnmappedWindow() { // this test verifies that creating the model just when an unmapped window exists doesn't cause problems // that is the unmapped window should be added (as expected), but also be removed again // create a window in "normal way" QSignalSpy windowCreatedSpy(m_pw, &PlasmaWindowManagement::windowCreated); QVERIFY(windowCreatedSpy.isValid()); auto w = m_pwInterface->createWindow(m_pwInterface); QVERIFY(w); QVERIFY(windowCreatedSpy.wait()); PlasmaWindow *window = windowCreatedSpy.first().first().value(); QVERIFY(window); // make sure the resource is properly created on server side QCoreApplication::instance()->processEvents(QEventLoop::WaitForMoreEvents); QSignalSpy unmappedSpy(window, &PlasmaWindow::unmapped); QVERIFY(unmappedSpy.isValid()); QSignalSpy destroyedSpy(window, &PlasmaWindow::destroyed); QVERIFY(destroyedSpy.isValid()); // unmap should be triggered, but not yet the destroyed w->unmap(); QVERIFY(unmappedSpy.wait()); QVERIFY(destroyedSpy.isEmpty()); auto model = m_pw->createWindowModel(); QVERIFY(model); QCOMPARE(model->rowCount(), 1); QSignalSpy rowRemovedSpy(model, &PlasmaWindowModel::rowsRemoved); QVERIFY(rowRemovedSpy.isValid()); QVERIFY(rowRemovedSpy.wait()); QCOMPARE(rowRemovedSpy.count(), 1); QCOMPARE(model->rowCount(), 0); QCOMPARE(destroyedSpy.count(), 1); } void PlasmaWindowModelTest::testChangeWindowAfterModelDestroy_data() { QTest::addColumn("changedSignal"); QTest::addColumn("setter"); QTest::addColumn("value"); QTest::newRow("active") << &PlasmaWindow::activeChanged << qVariantFromValue(&PlasmaWindowInterface::setActive) << QVariant(true); QTest::newRow("minimized") << &PlasmaWindow::minimizedChanged << qVariantFromValue(&PlasmaWindowInterface::setMinimized) << QVariant(true); QTest::newRow("fullscreen") << &PlasmaWindow::fullscreenChanged << qVariantFromValue(&PlasmaWindowInterface::setFullscreen) << QVariant(true); QTest::newRow("keepAbove") << &PlasmaWindow::keepAboveChanged << qVariantFromValue(&PlasmaWindowInterface::setKeepAbove) << QVariant(true); QTest::newRow("keepBelow") << &PlasmaWindow::keepBelowChanged << qVariantFromValue(&PlasmaWindowInterface::setKeepBelow) << QVariant(true); QTest::newRow("maximized") << &PlasmaWindow::maximizedChanged << qVariantFromValue(&PlasmaWindowInterface::setMaximized) << QVariant(true); QTest::newRow("demandsAttention") << &PlasmaWindow::demandsAttentionChanged << qVariantFromValue(&PlasmaWindowInterface::setDemandsAttention) << QVariant(true); QTest::newRow("closeable") << &PlasmaWindow::closeableChanged << qVariantFromValue(&PlasmaWindowInterface::setCloseable) << QVariant(true); QTest::newRow("minimizeable") << &PlasmaWindow::minimizeableChanged << qVariantFromValue(&PlasmaWindowInterface::setMinimizeable) << QVariant(true); QTest::newRow("maximizeable") << &PlasmaWindow::maximizeableChanged << qVariantFromValue(&PlasmaWindowInterface::setMaximizeable) << QVariant(true); QTest::newRow("fullscreenable") << &PlasmaWindow::fullscreenableChanged << qVariantFromValue(&PlasmaWindowInterface::setFullscreenable) << QVariant(true); QTest::newRow("skipTaskbar") << &PlasmaWindow::skipTaskbarChanged << qVariantFromValue(&PlasmaWindowInterface::setSkipTaskbar) << QVariant(true); QTest::newRow("shadeable") << &PlasmaWindow::shadeableChanged << qVariantFromValue(&PlasmaWindowInterface::setShadeable) << QVariant(true); QTest::newRow("shaded") << &PlasmaWindow::shadedChanged << qVariantFromValue(&PlasmaWindowInterface::setShaded) << QVariant(true); QTest::newRow("movable") << &PlasmaWindow::movableChanged << qVariantFromValue(&PlasmaWindowInterface::setMovable) << QVariant(true); QTest::newRow("resizable") << &PlasmaWindow::resizableChanged << qVariantFromValue(&PlasmaWindowInterface::setResizable) << QVariant(true); QTest::newRow("vdChangeable") << &PlasmaWindow::virtualDesktopChangeableChanged << qVariantFromValue(&PlasmaWindowInterface::setVirtualDesktopChangeable) << QVariant(true); QTest::newRow("onallDesktop") << &PlasmaWindow::onAllDesktopsChanged << qVariantFromValue(&PlasmaWindowInterface::setOnAllDesktops) << QVariant(true); QTest::newRow("title") << &PlasmaWindow::titleChanged << qVariantFromValue(&PlasmaWindowInterface::setTitle) << QVariant(QStringLiteral("foo")); QTest::newRow("appId") << &PlasmaWindow::appIdChanged << qVariantFromValue(&PlasmaWindowInterface::setAppId) << QVariant(QStringLiteral("foo")); QTest::newRow("icon" ) << &PlasmaWindow::iconChanged << qVariantFromValue(&PlasmaWindowInterface::setThemedIconName) << QVariant(QStringLiteral("foo")); QTest::newRow("vd") << &PlasmaWindow::virtualDesktopChanged << qVariantFromValue(&PlasmaWindowInterface::setVirtualDesktop) << QVariant(2u); QTest::newRow("unmapped") << &PlasmaWindow::unmapped << qVariantFromValue(&PlasmaWindowInterface::unmap) << QVariant(); } void PlasmaWindowModelTest::testChangeWindowAfterModelDestroy() { // this test verifies that changes in a window after the model got destroyed doesn't crash auto model = m_pw->createWindowModel(); QVERIFY(model); QSignalSpy windowCreatedSpy(m_pw, &PlasmaWindowManagement::windowCreated); QVERIFY(windowCreatedSpy.isValid()); auto w = m_pwInterface->createWindow(m_pwInterface); QVERIFY(windowCreatedSpy.wait()); PlasmaWindow *window = windowCreatedSpy.first().first().value(); // make sure the resource is properly created on server side QCoreApplication::instance()->processEvents(QEventLoop::WaitForMoreEvents); QCOMPARE(model->rowCount(), 1); delete model; QFETCH(ClientWindowSignal, changedSignal); QSignalSpy changedSpy(window, changedSignal); QVERIFY(changedSpy.isValid()); QVERIFY(!window->isActive()); QFETCH(QVariant, setter); QFETCH(QVariant, value); if (QMetaType::Type(value.type()) == QMetaType::Bool) { (w->*(setter.value()))(value.toBool()); } else if (QMetaType::Type(value.type()) == QMetaType::QString) { (w->*(setter.value()))(value.toString()); } else if (QMetaType::Type(value.type()) == QMetaType::UInt) { (w->*(setter.value()))(value.toUInt()); } else if (!value.isValid()) { (w->*(setter.value()))(); } QVERIFY(changedSpy.wait()); } void PlasmaWindowModelTest::testCreateWindowAfterModelDestroy() { // this test verifies that creating a window after the model got destroyed doesn't crash auto model = m_pw->createWindowModel(); QVERIFY(model); delete model; QSignalSpy windowCreatedSpy(m_pw, &PlasmaWindowManagement::windowCreated); QVERIFY(windowCreatedSpy.isValid()); m_pwInterface->createWindow(m_pwInterface); QVERIFY(windowCreatedSpy.wait()); } QTEST_GUILESS_MAIN(PlasmaWindowModelTest) #include "test_plasma_window_model.moc" diff --git a/autotests/client/test_wayland_windowmanagement.cpp b/autotests/client/test_wayland_windowmanagement.cpp index 662f7dc..22679ac 100644 --- a/autotests/client/test_wayland_windowmanagement.cpp +++ b/autotests/client/test_wayland_windowmanagement.cpp @@ -1,605 +1,604 @@ /******************************************************************** Copyright 2015 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/plasmawindowmanagement.h" #include "../../src/client/surface.h" #include "../../src/server/display.h" #include "../../src/server/compositor_interface.h" #include "../../src/server/region_interface.h" #include "../../src/server/plasmawindowmanagement_interface.h" #include "../../src/server/surface_interface.h" #include typedef void (KWayland::Server::PlasmaWindowInterface::*ServerWindowSignal)(); Q_DECLARE_METATYPE(ServerWindowSignal) typedef void (KWayland::Server::PlasmaWindowInterface::*ServerWindowBooleanSignal)(bool); Q_DECLARE_METATYPE(ServerWindowBooleanSignal) typedef void (KWayland::Client::PlasmaWindow::*ClientWindowVoidSetter)(); Q_DECLARE_METATYPE(ClientWindowVoidSetter) class TestWindowManagement : public QObject { Q_OBJECT public: explicit TestWindowManagement(QObject *parent = nullptr); private Q_SLOTS: void init(); void testWindowTitle(); void testMinimizedGeometry(); void testUseAfterUnmap(); void testServerDelete(); void testActiveWindowOnUnmapped(); void testDeleteActiveWindow(); void testCreateAfterUnmap(); void testRequests_data(); void testRequests(); void testRequestsBoolean_data(); void testRequestsBoolean(); void testShowingDesktop(); void testRequestShowingDesktop_data(); void testRequestShowingDesktop(); void testParentWindow(); void testGeometry(); void testIcon(); void testPid(); void cleanup(); private: KWayland::Server::Display *m_display; KWayland::Server::CompositorInterface *m_compositorInterface; KWayland::Server::PlasmaWindowManagementInterface *m_windowManagementInterface; KWayland::Server::PlasmaWindowInterface *m_windowInterface; KWayland::Server::SurfaceInterface *m_surfaceInterface = nullptr; KWayland::Client::Surface *m_surface = nullptr; KWayland::Client::ConnectionThread *m_connection; KWayland::Client::Compositor *m_compositor; KWayland::Client::EventQueue *m_queue; KWayland::Client::PlasmaWindowManagement *m_windowManagement; KWayland::Client::PlasmaWindow *m_window; QThread *m_thread; KWayland::Client::Registry *m_registry; }; static const QString s_socketName = QStringLiteral("kwayland-test-wayland-windowmanagement-0"); TestWindowManagement::TestWindowManagement(QObject *parent) : QObject(parent) , m_display(nullptr) , m_compositorInterface(nullptr) , m_connection(nullptr) , m_compositor(nullptr) , m_queue(nullptr) , m_thread(nullptr) { } void TestWindowManagement::init() { using namespace KWayland::Server; qRegisterMetaType("ShowingDesktopState"); delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, SIGNAL(connected())); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new KWayland::Client::EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); m_registry = new KWayland::Client::Registry(this); QSignalSpy compositorSpy(m_registry, SIGNAL(compositorAnnounced(quint32,quint32))); QVERIFY(compositorSpy.isValid()); QSignalSpy windowManagementSpy(m_registry, SIGNAL(plasmaWindowManagementAnnounced(quint32,quint32))); QVERIFY(windowManagementSpy.isValid()); QVERIFY(!m_registry->eventQueue()); m_registry->setEventQueue(m_queue); QCOMPARE(m_registry->eventQueue(), m_queue); m_registry->create(m_connection->display()); QVERIFY(m_registry->isValid()); m_registry->setup(); m_compositorInterface = m_display->createCompositor(m_display); m_compositorInterface->create(); QVERIFY(m_compositorInterface->isValid()); QVERIFY(compositorSpy.wait()); m_compositor = m_registry->createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); m_windowManagementInterface = m_display->createPlasmaWindowManagement(m_display); m_windowManagementInterface->create(); QVERIFY(m_windowManagementInterface->isValid()); QVERIFY(windowManagementSpy.wait()); m_windowManagement = m_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(); QSignalSpy serverSurfaceCreated(m_compositorInterface, SIGNAL(surfaceCreated(KWayland::Server::SurfaceInterface*))); QVERIFY(serverSurfaceCreated.isValid()); m_surface = m_compositor->createSurface(this); QVERIFY(m_surface); QVERIFY(serverSurfaceCreated.wait()); m_surfaceInterface = serverSurfaceCreated.first().first().value(); QVERIFY(m_surfaceInterface); } void TestWindowManagement::testWindowTitle() { m_windowInterface->setTitle(QStringLiteral("Test Title")); QSignalSpy titleSpy(m_window, SIGNAL(titleChanged())); QVERIFY(titleSpy.isValid()); QVERIFY(titleSpy.wait()); QCOMPARE(m_window->title(), QString::fromUtf8("Test Title")); } void TestWindowManagement::testMinimizedGeometry() { m_window->setMinimizedGeometry(m_surface, QRect(5, 10, 100, 200)); QSignalSpy geometrySpy(m_windowInterface, SIGNAL(minimizedGeometriesChanged())); QVERIFY(geometrySpy.isValid()); QVERIFY(geometrySpy.wait()); QCOMPARE(m_windowInterface->minimizedGeometries().values().first(), QRect(5, 10, 100, 200)); m_window->unsetMinimizedGeometry(m_surface); QVERIFY(geometrySpy.wait()); QVERIFY(m_windowInterface->minimizedGeometries().isEmpty()); } void TestWindowManagement::cleanup() { if (m_surface) { delete m_surface; m_surface = nullptr; } if (m_compositor) { delete m_compositor; m_compositor = nullptr; } if (m_queue) { delete m_queue; m_queue = nullptr; } if (m_windowManagement) { delete m_windowManagement; m_windowManagement = nullptr; } if (m_registry) { delete m_registry; m_registry = nullptr; } if (m_thread) { if (m_connection) { m_connection->flush(); m_connection->deleteLater(); } m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } m_connection = nullptr; m_display->dispatchEvents(); delete m_windowManagementInterface; m_windowManagementInterface = nullptr; delete m_windowInterface; m_windowInterface = nullptr; delete m_surfaceInterface; m_surfaceInterface = nullptr; delete m_display; m_display = nullptr; } void TestWindowManagement::testUseAfterUnmap() { // this test verifies that when the client uses a window after it's unmapped, things don't break QSignalSpy unmappedSpy(m_window, &KWayland::Client::PlasmaWindow::unmapped); QVERIFY(unmappedSpy.isValid()); QSignalSpy destroyedSpy(m_window, &QObject::destroyed); QVERIFY(destroyedSpy.isValid()); m_windowInterface->unmap(); m_window->requestClose(); QVERIFY(unmappedSpy.wait()); QVERIFY(destroyedSpy.wait()); m_window = nullptr; QSignalSpy serverDestroyedSpy(m_windowInterface, &QObject::destroyed); QVERIFY(serverDestroyedSpy.isValid()); QVERIFY(serverDestroyedSpy.wait()); m_windowInterface = nullptr; } void TestWindowManagement::testServerDelete() { QSignalSpy unmappedSpy(m_window, &KWayland::Client::PlasmaWindow::unmapped); QVERIFY(unmappedSpy.isValid()); QSignalSpy destroyedSpy(m_window, &QObject::destroyed); QVERIFY(destroyedSpy.isValid()); delete m_windowInterface; m_windowInterface = nullptr; QVERIFY(unmappedSpy.wait()); QVERIFY(destroyedSpy.wait()); m_window = nullptr; } void TestWindowManagement::testActiveWindowOnUnmapped() { // This test verifies that unmapping the active window changes the active window. // first make the window active QVERIFY(!m_windowManagement->activeWindow()); QVERIFY(!m_window->isActive()); QSignalSpy activeWindowChangedSpy(m_windowManagement, &KWayland::Client::PlasmaWindowManagement::activeWindowChanged); QVERIFY(activeWindowChangedSpy.isValid()); m_windowInterface->setActive(true); QVERIFY(activeWindowChangedSpy.wait()); QCOMPARE(m_windowManagement->activeWindow(), m_window); QVERIFY(m_window->isActive()); // now unmap should change the active window QSignalSpy destroyedSpy(m_window, &QObject::destroyed); QVERIFY(destroyedSpy.isValid()); QSignalSpy serverDestroyedSpy(m_windowInterface, &QObject::destroyed); QVERIFY(serverDestroyedSpy.isValid()); m_windowManagementInterface->unmapWindow(m_windowInterface); QVERIFY(activeWindowChangedSpy.wait()); QVERIFY(!m_windowManagement->activeWindow()); QVERIFY(destroyedSpy.wait()); QCOMPARE(destroyedSpy.count(), 1); m_window = nullptr; QVERIFY(serverDestroyedSpy.wait()); m_windowInterface = nullptr; } void TestWindowManagement::testDeleteActiveWindow() { // This test verifies that deleting the active window on client side changes the active window // first make the window active QVERIFY(!m_windowManagement->activeWindow()); QVERIFY(!m_window->isActive()); QSignalSpy activeWindowChangedSpy(m_windowManagement, &KWayland::Client::PlasmaWindowManagement::activeWindowChanged); QVERIFY(activeWindowChangedSpy.isValid()); m_windowInterface->setActive(true); QVERIFY(activeWindowChangedSpy.wait()); QCOMPARE(activeWindowChangedSpy.count(), 1); QCOMPARE(m_windowManagement->activeWindow(), m_window); QVERIFY(m_window->isActive()); // delete the client side window - that's semantically kind of wrong, but shouldn't make our code crash delete m_window; m_window = nullptr; QCOMPARE(activeWindowChangedSpy.count(), 2); QVERIFY(!m_windowManagement->activeWindow()); } void TestWindowManagement::testCreateAfterUnmap() { // this test verifies that we don't get a protocol error on client side when creating an already unmapped window. QCOMPARE(m_windowManagement->children().count(), 1); // create and unmap in one go // client will first handle the create, the unmap will be sent once the server side is bound auto serverWindow = m_windowManagementInterface->createWindow(this); serverWindow->unmap(); QCOMPARE(m_windowManagementInterface->children().count(), 0); QCoreApplication::instance()->processEvents(); QCoreApplication::instance()->processEvents(QEventLoop::WaitForMoreEvents); QTRY_COMPARE(m_windowManagement->children().count(), 2); auto window = dynamic_cast(m_windowManagement->children().last()); QVERIFY(window); // now this is not yet on the server, on the server it will be after next roundtrip // which we can trigger by waiting for destroy of the newly created window. // why destroy? Because we will get the unmap which triggers a destroy QSignalSpy clientDestroyedSpy(window, &QObject::destroyed); QVERIFY(clientDestroyedSpy.isValid()); QVERIFY(clientDestroyedSpy.wait()); QCOMPARE(m_windowManagement->children().count(), 1); // the server side created a helper PlasmaWindowInterface with PlasmaWindowManagementInterface as parent // it emitted unmapped so we can be sure it will be destroyed directly QCOMPARE(m_windowManagementInterface->children().count(), 1); auto helperWindow = qobject_cast(m_windowManagementInterface->children().first()); QVERIFY(helperWindow); QSignalSpy helperDestroyedSpy(helperWindow, &QObject::destroyed); QVERIFY(helperDestroyedSpy.isValid()); QVERIFY(helperDestroyedSpy.wait()); } void TestWindowManagement::testRequests_data() { using namespace KWayland::Server; using namespace KWayland::Client; QTest::addColumn("changedSignal"); QTest::addColumn("requester"); QTest::newRow("close") << &PlasmaWindowInterface::closeRequested << &PlasmaWindow::requestClose; QTest::newRow("move") << &PlasmaWindowInterface::moveRequested << &PlasmaWindow::requestMove; QTest::newRow("resize") << &PlasmaWindowInterface::resizeRequested << &PlasmaWindow::requestResize; } void TestWindowManagement::testRequests() { // this test case verifies all the different requests on a PlasmaWindow QFETCH(ServerWindowSignal, changedSignal); QSignalSpy requestSpy(m_windowInterface, changedSignal); QVERIFY(requestSpy.isValid()); QFETCH(ClientWindowVoidSetter, requester); (m_window->*(requester))(); QVERIFY(requestSpy.wait()); } void TestWindowManagement::testRequestsBoolean_data() { using namespace KWayland::Server; QTest::addColumn("changedSignal"); QTest::addColumn("flag"); QTest::newRow("activate") << &PlasmaWindowInterface::activeRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE); QTest::newRow("minimized") << &PlasmaWindowInterface::minimizedRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED); QTest::newRow("maximized") << &PlasmaWindowInterface::maximizedRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED); QTest::newRow("fullscreen") << &PlasmaWindowInterface::fullscreenRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREEN); QTest::newRow("keepAbove") << &PlasmaWindowInterface::keepAboveRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE); QTest::newRow("keepBelow") << &PlasmaWindowInterface::keepBelowRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW); QTest::newRow("demandsAttention") << &PlasmaWindowInterface::demandsAttentionRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_DEMANDS_ATTENTION); QTest::newRow("closeable") << &PlasmaWindowInterface::closeableRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_CLOSEABLE); QTest::newRow("minimizable") << &PlasmaWindowInterface::minimizeableRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZABLE); 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("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); QTest::newRow("resizable") << &PlasmaWindowInterface::resizableRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_RESIZABLE); QTest::newRow("virtualDesktopChangeable") << &PlasmaWindowInterface::virtualDesktopChangeableRequested << int(ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_VIRTUAL_DESKTOP_CHANGEABLE); } void TestWindowManagement::testRequestsBoolean() { // this test case verifies all the different requests on a PlasmaWindow QFETCH(ServerWindowBooleanSignal, changedSignal); QSignalSpy requestSpy(m_windowInterface, changedSignal); QVERIFY(requestSpy.isValid()); QFETCH(int, flag); org_kde_plasma_window_set_state(*m_window, flag, flag); QVERIFY(requestSpy.wait()); QCOMPARE(requestSpy.count(), 1); QCOMPARE(requestSpy.first().first().toBool(), true); org_kde_plasma_window_set_state(*m_window, flag, 0); QVERIFY(requestSpy.wait()); QCOMPARE(requestSpy.count(), 2); QCOMPARE(requestSpy.last().first().toBool(), false); } void TestWindowManagement::testShowingDesktop() { using namespace KWayland::Server; // this test verifies setting the showing desktop state QVERIFY(!m_windowManagement->isShowingDesktop()); QSignalSpy showingDesktopChangedSpy(m_windowManagement, &KWayland::Client::PlasmaWindowManagement::showingDesktopChanged); QVERIFY(showingDesktopChangedSpy.isValid()); m_windowManagementInterface->setShowingDesktopState(PlasmaWindowManagementInterface::ShowingDesktopState::Enabled); QVERIFY(showingDesktopChangedSpy.wait()); QCOMPARE(showingDesktopChangedSpy.count(), 1); QCOMPARE(showingDesktopChangedSpy.first().first().toBool(), true); QVERIFY(m_windowManagement->isShowingDesktop()); // setting to same should not change m_windowManagementInterface->setShowingDesktopState(PlasmaWindowManagementInterface::ShowingDesktopState::Enabled); QVERIFY(!showingDesktopChangedSpy.wait(100)); QCOMPARE(showingDesktopChangedSpy.count(), 1); // setting to other state should change m_windowManagementInterface->setShowingDesktopState(PlasmaWindowManagementInterface::ShowingDesktopState::Disabled); QVERIFY(showingDesktopChangedSpy.wait()); QCOMPARE(showingDesktopChangedSpy.count(), 2); QCOMPARE(showingDesktopChangedSpy.first().first().toBool(), true); QCOMPARE(showingDesktopChangedSpy.last().first().toBool(), false); QVERIFY(!m_windowManagement->isShowingDesktop()); } void TestWindowManagement::testRequestShowingDesktop_data() { using namespace KWayland::Server; QTest::addColumn("value"); QTest::addColumn("expectedValue"); QTest::newRow("enable") << true << PlasmaWindowManagementInterface::ShowingDesktopState::Enabled; QTest::newRow("disable") << false << PlasmaWindowManagementInterface::ShowingDesktopState::Disabled; } void TestWindowManagement::testRequestShowingDesktop() { // this test verifies requesting show desktop state using namespace KWayland::Server; QSignalSpy requestSpy(m_windowManagementInterface, &PlasmaWindowManagementInterface::requestChangeShowingDesktop); QVERIFY(requestSpy.isValid()); QFETCH(bool, value); m_windowManagement->setShowingDesktop(value); QVERIFY(requestSpy.wait()); QCOMPARE(requestSpy.count(), 1); QTEST(requestSpy.first().first().value(), "expectedValue"); } void TestWindowManagement::testParentWindow() { using namespace KWayland::Client; // this test verifies the functionality of ParentWindows QCOMPARE(m_windowManagement->windows().count(), 1); auto parentWindow = m_windowManagement->windows().first(); QVERIFY(parentWindow); QVERIFY(parentWindow->parentWindow().isNull()); // now let's create a second window QSignalSpy windowAddedSpy(m_windowManagement, &PlasmaWindowManagement::windowCreated); QVERIFY(windowAddedSpy.isValid()); auto serverTransient = m_windowManagementInterface->createWindow(this); serverTransient->setParentWindow(m_windowInterface); QVERIFY(windowAddedSpy.wait()); auto transient = windowAddedSpy.first().first().value(); QCOMPARE(transient->parentWindow().data(), parentWindow); // let's unset the parent QSignalSpy parentWindowChangedSpy(transient, &PlasmaWindow::parentWindowChanged); QVERIFY(parentWindowChangedSpy.isValid()); serverTransient->setParentWindow(nullptr); QVERIFY(parentWindowChangedSpy.wait()); QVERIFY(transient->parentWindow().isNull()); // and set it again serverTransient->setParentWindow(m_windowInterface); QVERIFY(parentWindowChangedSpy.wait()); QCOMPARE(transient->parentWindow().data(), parentWindow); // now let's try to unmap the parent m_windowInterface->unmap(); m_window = nullptr; m_windowInterface = nullptr; QVERIFY(parentWindowChangedSpy.wait()); QVERIFY(transient->parentWindow().isNull()); } void TestWindowManagement::testGeometry() { using namespace KWayland::Client; QVERIFY(m_window); QCOMPARE(m_window->geometry(), QRect()); QSignalSpy windowGeometryChangedSpy(m_window, &PlasmaWindow::geometryChanged); QVERIFY(windowGeometryChangedSpy.isValid()); m_windowInterface->setGeometry(QRect(20, -10, 30, 40)); QVERIFY(windowGeometryChangedSpy.wait()); QCOMPARE(m_window->geometry(), QRect(20, -10, 30, 40)); // setting an empty geometry should not be sent to the client m_windowInterface->setGeometry(QRect()); QVERIFY(!windowGeometryChangedSpy.wait(10)); // setting to the geometry which the client still has should not trigger signal m_windowInterface->setGeometry(QRect(20, -10, 30, 40)); QVERIFY(!windowGeometryChangedSpy.wait(10)); // setting another geometry should work, though m_windowInterface->setGeometry(QRect(0, 0, 35, 45)); QVERIFY(windowGeometryChangedSpy.wait()); QCOMPARE(windowGeometryChangedSpy.count(), 2); QCOMPARE(m_window->geometry(), QRect(0, 0, 35, 45)); // let's bind a second PlasmaWindowManagement to verify the initial setting QScopedPointer pm(m_registry->createPlasmaWindowManagement(m_registry->interface(Registry::Interface::PlasmaWindowManagement).name, m_registry->interface(Registry::Interface::PlasmaWindowManagement).version)); QVERIFY(!pm.isNull()); QSignalSpy windowAddedSpy(pm.data(), &PlasmaWindowManagement::windowCreated); QVERIFY(windowAddedSpy.isValid()); QVERIFY(windowAddedSpy.wait()); auto window = pm->windows().first(); QCOMPARE(window->geometry(), QRect(0, 0, 35, 45)); } void TestWindowManagement::testIcon() { using namespace KWayland::Client; QVERIFY(m_window); QSignalSpy iconChangedSpy(m_window, &PlasmaWindow::iconChanged); QVERIFY(iconChangedSpy.isValid()); m_windowInterface->setIcon(QIcon()); // first goes from themed name to empty QVERIFY(iconChangedSpy.wait()); if (!QIcon::hasThemeIcon(QStringLiteral("wayland"))) { QEXPECT_FAIL("", "no icon", Continue); } QCOMPARE(m_window->icon().name(), QStringLiteral("wayland")); QVERIFY(iconChangedSpy.wait()); if (!QIcon::hasThemeIcon(QStringLiteral("wayland"))) { QEXPECT_FAIL("", "no icon", Continue); } QCOMPARE(m_window->icon().name(), QStringLiteral("wayland")); // create an icon with a pixmap QPixmap p(32, 32); p.fill(Qt::red); m_windowInterface->setIcon(p); QVERIFY(iconChangedSpy.wait()); QCOMPARE(m_window->icon().pixmap(32, 32), p); // let's set a themed icon m_windowInterface->setIcon(QIcon::fromTheme(QStringLiteral("xorg"))); QVERIFY(iconChangedSpy.wait()); if (!QIcon::hasThemeIcon(QStringLiteral("xorg"))) { QEXPECT_FAIL("", "no icon", Continue); } QCOMPARE(m_window->icon().name(), QStringLiteral("xorg")); } void TestWindowManagement::testPid() { using namespace KWayland::Client; QVERIFY(m_window); - QVERIFY(m_windowInterface); - QVERIFY(m_window->pid() == 0); - QSignalSpy pidChangedSpy(m_window, &PlasmaWindow::pidChanged); - QVERIFY(pidChangedSpy.isValid()); - // doing nothing does nothing - QVERIFY(!pidChangedSpy.wait(10)); - m_windowInterface->setPid(1984); - QVERIFY(pidChangedSpy.wait()); - QVERIFY(m_window->pid() == 1984); - // no signal when the same value is set twice - m_windowInterface->setPid(1984); - QVERIFY(!pidChangedSpy.wait(10)); - QVERIFY(m_window->pid() == 1984); + QVERIFY(m_window->pid() == 1337); + + //test server not setting a PID for whatever reason + QScopedPointer newWindowInterface(m_windowManagementInterface->createWindow(this)); + QSignalSpy windowSpy(m_windowManagement, SIGNAL(windowCreated(KWayland::Client::PlasmaWindow *))); + QVERIFY(windowSpy.wait()); + QScopedPointer newWindow( windowSpy.first().first().value()); + QVERIFY(newWindow); + QVERIFY(newWindow->pid() == 0); + + } QTEST_MAIN(TestWindowManagement) #include "test_wayland_windowmanagement.moc" diff --git a/src/client/plasmawindowmanagement.cpp b/src/client/plasmawindowmanagement.cpp index 5b7e952..c9a8834 100644 --- a/src/client/plasmawindowmanagement.cpp +++ b/src/client/plasmawindowmanagement.cpp @@ -1,970 +1,969 @@ /******************************************************************** Copyright 2015 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #include "plasmawindowmanagement.h" #include "plasmawindowmodel.h" #include "event_queue.h" #include "output.h" #include "surface.h" #include "wayland_pointer_p.h" // Wayland #include #include #include #include #include #include namespace KWayland { namespace Client { class PlasmaWindowManagement::Private { public: Private(PlasmaWindowManagement *q); WaylandPointer wm; EventQueue *queue = nullptr; bool showingDesktop = false; QList windows; PlasmaWindow *activeWindow = nullptr; void setup(org_kde_plasma_window_management *wm); private: static void showDesktopCallback(void *data, org_kde_plasma_window_management *org_kde_plasma_window_management, uint32_t state); static void windowCallback(void *data, org_kde_plasma_window_management *org_kde_plasma_window_management, uint32_t id); void setShowDesktop(bool set); void windowCreated(org_kde_plasma_window *id, quint32 internalId); static struct org_kde_plasma_window_management_listener s_listener; PlasmaWindowManagement *q; }; class PlasmaWindow::Private { public: Private(org_kde_plasma_window *window, quint32 internalId, PlasmaWindow *q); WaylandPointer window; quint32 internalId; QString title; QString appId; quint32 desktop = 0; bool active = false; bool minimized = false; bool maximized = false; bool fullscreen = false; bool keepAbove = false; bool keepBelow = false; bool onAllDesktops = false; bool demandsAttention = false; bool closeable = false; bool minimizeable = false; bool maximizeable = false; bool fullscreenable = false; bool skipTaskbar = false; bool shadeable = false; bool shaded = false; bool movable = false; bool resizable = false; bool virtualDesktopChangeable = false; QIcon icon; PlasmaWindowManagement *wm = nullptr; bool unmapped = false; QPointer parentWindow; QMetaObject::Connection parentWindowUnmappedConnection; QRect geometry; quint32 pid = 0; private: static void titleChangedCallback(void *data, org_kde_plasma_window *window, const char *title); static void appIdChangedCallback(void *data, org_kde_plasma_window *window, const char *app_id); static void pidChangedCallback(void *data, org_kde_plasma_window *window, uint32_t pid); static void stateChangedCallback(void *data, org_kde_plasma_window *window, uint32_t state); static void virtualDesktopChangedCallback(void *data, org_kde_plasma_window *window, int32_t number); static void themedIconNameChangedCallback(void *data, org_kde_plasma_window *window, const char *name); static void unmappedCallback(void *data, org_kde_plasma_window *window); static void initialStateCallback(void *data, org_kde_plasma_window *window); static void parentWindowCallback(void *data, org_kde_plasma_window *window, org_kde_plasma_window *parent); static void windowGeometryCallback(void *data, org_kde_plasma_window *window, int32_t x, int32_t y, uint32_t width, uint32_t height); static void iconChangedCallback(void *data, org_kde_plasma_window *org_kde_plasma_window); void setActive(bool set); void setMinimized(bool set); void setMaximized(bool set); void setFullscreen(bool set); void setKeepAbove(bool set); void setKeepBelow(bool set); void setOnAllDesktops(bool set); void setDemandsAttention(bool set); void setCloseable(bool set); void setMinimizeable(bool set); void setMaximizeable(bool set); void setFullscreenable(bool set); void setSkipTaskbar(bool skip); void setShadeable(bool set); void setShaded(bool set); void setMovable(bool set); void setResizable(bool set); void setVirtualDesktopChangeable(bool set); void setParentWindow(PlasmaWindow *parentWindow); void setPid(const quint32 pid); static Private *cast(void *data) { return reinterpret_cast(data); } PlasmaWindow *q; static struct org_kde_plasma_window_listener s_listener; }; PlasmaWindowManagement::Private::Private(PlasmaWindowManagement *q) : q(q) { } org_kde_plasma_window_management_listener PlasmaWindowManagement::Private::s_listener = { showDesktopCallback, windowCallback }; void PlasmaWindowManagement::Private::setup(org_kde_plasma_window_management *windowManagement) { Q_ASSERT(!wm); Q_ASSERT(windowManagement); wm.setup(windowManagement); org_kde_plasma_window_management_add_listener(windowManagement, &s_listener, this); } void PlasmaWindowManagement::Private::showDesktopCallback(void *data, org_kde_plasma_window_management *org_kde_plasma_window_management, uint32_t state) { auto wm = reinterpret_cast(data); Q_ASSERT(wm->wm == org_kde_plasma_window_management); switch (state) { case ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_ENABLED: wm->setShowDesktop(true); break; case ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_DISABLED: wm->setShowDesktop(false); break; default: Q_UNREACHABLE(); break; } } void PlasmaWindowManagement::Private::setShowDesktop(bool set) { if (showingDesktop == set) { return; } showingDesktop = set; emit q->showingDesktopChanged(showingDesktop); } void PlasmaWindowManagement::Private::windowCallback(void *data, org_kde_plasma_window_management *interface, uint32_t id) { auto wm = reinterpret_cast(data); Q_ASSERT(wm->wm == interface); QTimer *timer = new QTimer(); timer->setSingleShot(true); timer->setInterval(0); QObject::connect(timer, &QTimer::timeout, wm->q, [timer, wm, id] { wm->windowCreated(org_kde_plasma_window_management_get_window(wm->wm, id), id); timer->deleteLater(); }, Qt::QueuedConnection ); timer->start(); } void PlasmaWindowManagement::Private::windowCreated(org_kde_plasma_window *id, quint32 internalId) { if (queue) { queue->addProxy(id); } PlasmaWindow *window = new PlasmaWindow(q, id, internalId); window->d->wm = q; windows << window; QObject::connect(window, &QObject::destroyed, q, [this, window] { windows.removeAll(window); if (activeWindow == window) { activeWindow = nullptr; emit q->activeWindowChanged(); } } ); QObject::connect(window, &PlasmaWindow::unmapped, q, [this, window] { if (activeWindow == window) { activeWindow = nullptr; emit q->activeWindowChanged(); } } ); QObject::connect(window, &PlasmaWindow::activeChanged, q, [this, window] { if (window->isActive()) { if (activeWindow == window) { return; } activeWindow = window; emit q->activeWindowChanged(); } else { if (activeWindow == window) { activeWindow = nullptr; emit q->activeWindowChanged(); } } } ); } PlasmaWindowManagement::PlasmaWindowManagement(QObject *parent) : QObject(parent) , d(new Private(this)) { } PlasmaWindowManagement::~PlasmaWindowManagement() { release(); } void PlasmaWindowManagement::destroy() { if (!d->wm) { return; } emit interfaceAboutToBeDestroyed(); d->wm.destroy(); } void PlasmaWindowManagement::release() { if (!d->wm) { return; } emit interfaceAboutToBeReleased(); d->wm.release(); } void PlasmaWindowManagement::setup(org_kde_plasma_window_management *wm) { d->setup(wm); } void PlasmaWindowManagement::setEventQueue(EventQueue *queue) { d->queue = queue; } EventQueue *PlasmaWindowManagement::eventQueue() { return d->queue; } bool PlasmaWindowManagement::isValid() const { return d->wm.isValid(); } PlasmaWindowManagement::operator org_kde_plasma_window_management*() { return d->wm; } PlasmaWindowManagement::operator org_kde_plasma_window_management*() const { return d->wm; } void PlasmaWindowManagement::hideDesktop() { setShowingDesktop(false); } void PlasmaWindowManagement::showDesktop() { setShowingDesktop(true); } void PlasmaWindowManagement::setShowingDesktop(bool show) { org_kde_plasma_window_management_show_desktop(d->wm, show ? ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_ENABLED : ORG_KDE_PLASMA_WINDOW_MANAGEMENT_SHOW_DESKTOP_DISABLED); } bool PlasmaWindowManagement::isShowingDesktop() const { return d->showingDesktop; } QList< PlasmaWindow* > PlasmaWindowManagement::windows() const { return d->windows; } PlasmaWindow *PlasmaWindowManagement::activeWindow() const { return d->activeWindow; } PlasmaWindowModel *PlasmaWindowManagement::createWindowModel() { return new PlasmaWindowModel(this); } org_kde_plasma_window_listener PlasmaWindow::Private::s_listener = { titleChangedCallback, appIdChangedCallback, stateChangedCallback, virtualDesktopChangedCallback, themedIconNameChangedCallback, unmappedCallback, initialStateCallback, parentWindowCallback, windowGeometryCallback, iconChangedCallback, pidChangedCallback }; void PlasmaWindow::Private::parentWindowCallback(void *data, org_kde_plasma_window *window, org_kde_plasma_window *parent) { Q_UNUSED(window) Private *p = cast(data); const auto windows = p->wm->windows(); auto it = std::find_if(windows.begin(), windows.end(), [parent] (const PlasmaWindow *w) { return *w == parent; } ); p->setParentWindow(it != windows.end() ? *it : nullptr); } void PlasmaWindow::Private::windowGeometryCallback(void *data, org_kde_plasma_window *window, int32_t x, int32_t y, uint32_t width, uint32_t height) { Q_UNUSED(window) Private *p = cast(data); QRect geo(x, y, width, height); if (geo == p->geometry) { return; } p->geometry = geo; emit p->q->geometryChanged(); } void PlasmaWindow::Private::setParentWindow(PlasmaWindow *parent) { const auto old = parentWindow; QObject::disconnect(parentWindowUnmappedConnection); if (parent && !parent->d->unmapped) { parentWindow = QPointer(parent); parentWindowUnmappedConnection = QObject::connect(parent, &PlasmaWindow::unmapped, q, [this] { setParentWindow(nullptr); } ); } else { parentWindow = QPointer(); parentWindowUnmappedConnection = QMetaObject::Connection(); } if (parentWindow.data() != old.data()) { emit q->parentWindowChanged(); } } void PlasmaWindow::Private::initialStateCallback(void *data, org_kde_plasma_window *window) { Q_UNUSED(window) Private *p = cast(data); if (!p->unmapped) { emit p->wm->windowCreated(p->q); } } void PlasmaWindow::Private::titleChangedCallback(void *data, org_kde_plasma_window *window, const char *title) { Q_UNUSED(window) Private *p = cast(data); const QString t = QString::fromUtf8(title); if (p->title == t) { return; } p->title = t; emit p->q->titleChanged(); } void PlasmaWindow::Private::appIdChangedCallback(void *data, org_kde_plasma_window *window, const char *appId) { Q_UNUSED(window) Private *p = cast(data); const QString s = QString::fromUtf8(appId); if (s == p->appId) { return; } p->appId = s; emit p->q->appIdChanged(); } void PlasmaWindow::Private::pidChangedCallback(void *data, org_kde_plasma_window *window, uint32_t pid) { Q_UNUSED(window) Private *p = cast(data); if (p->pid == static_cast(pid)) { return; } p->pid = pid; - emit p->q->pidChanged(); } void PlasmaWindow::Private::virtualDesktopChangedCallback(void *data, org_kde_plasma_window *window, int32_t number) { Q_UNUSED(window) Private *p = cast(data); if (p->desktop == static_cast(number)) { return; } p->desktop = number; emit p->q->virtualDesktopChanged(); } void PlasmaWindow::Private::unmappedCallback(void *data, org_kde_plasma_window *window) { auto p = cast(data); Q_UNUSED(window); p->unmapped = true; emit p->q->unmapped(); p->q->deleteLater(); } void PlasmaWindow::Private::stateChangedCallback(void *data, org_kde_plasma_window *window, uint32_t state) { auto p = cast(data); Q_UNUSED(window); p->setActive(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE); p->setMinimized(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED); p->setMaximized(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED); p->setFullscreen(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREEN); p->setKeepAbove(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_ABOVE); p->setKeepBelow(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_KEEP_BELOW); p->setOnAllDesktops(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ON_ALL_DESKTOPS); p->setDemandsAttention(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_DEMANDS_ATTENTION); p->setCloseable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_CLOSEABLE); p->setFullscreenable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_FULLSCREENABLE); p->setMaximizeable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZABLE); p->setMinimizeable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZABLE); p->setSkipTaskbar(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SKIPTASKBAR); p->setShadeable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADEABLE); p->setShaded(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED); p->setMovable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MOVABLE); p->setResizable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_RESIZABLE); p->setVirtualDesktopChangeable(state & ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_VIRTUAL_DESKTOP_CHANGEABLE); } void PlasmaWindow::Private::themedIconNameChangedCallback(void *data, org_kde_plasma_window *window, const char *name) { auto p = cast(data); Q_UNUSED(window); const QString themedName = QString::fromUtf8(name); if (!themedName.isEmpty()) { QIcon icon = QIcon::fromTheme(themedName); p->icon = icon; } else { p->icon = QIcon(); } emit p->q->iconChanged(); } static int readData(int fd, QByteArray &data) { // implementation based on QtWayland file qwaylanddataoffer.cpp char buf[4096]; int retryCount = 0; int n; while (true) { n = QT_READ(fd, buf, sizeof buf); if (n == -1 && (errno == EAGAIN) && ++retryCount < 1000) { usleep(1000); } else { break; } } if (n > 0) { data.append(buf, n); n = readData(fd, data); } return n; } void PlasmaWindow::Private::iconChangedCallback(void *data, org_kde_plasma_window *window) { auto p = cast(data); Q_UNUSED(window); int pipeFds[2]; if (pipe2(pipeFds, O_CLOEXEC|O_NONBLOCK) != 0) { return; } org_kde_plasma_window_get_icon(p->window, pipeFds[1]); close(pipeFds[1]); const int pipeFd = pipeFds[0]; auto readIcon = [pipeFd] () -> QIcon { QByteArray content; if (readData(pipeFd, content) != 0) { close(pipeFd); return QIcon::fromTheme(QStringLiteral("wayland")); } close(pipeFd); QDataStream ds(content); QIcon icon; ds >> icon; if (icon.isNull()) { return QIcon::fromTheme(QStringLiteral("wayland")); } return icon; }; QFutureWatcher *watcher = new QFutureWatcher(p->q); QObject::connect(watcher, &QFutureWatcher::finished, p->q, [p, watcher] { watcher->deleteLater(); p->icon = watcher->result(); emit p->q->iconChanged(); } ); watcher->setFuture(QtConcurrent::run(readIcon)); } void PlasmaWindow::Private::setActive(bool set) { if (active == set) { return; } active = set; emit q->activeChanged(); } void PlasmaWindow::Private::setFullscreen(bool set) { if (fullscreen == set) { return; } fullscreen = set; emit q->fullscreenChanged(); } void PlasmaWindow::Private::setKeepAbove(bool set) { if (keepAbove == set) { return; } keepAbove = set; emit q->keepAboveChanged(); } void PlasmaWindow::Private::setKeepBelow(bool set) { if (keepBelow == set) { return; } keepBelow = set; emit q->keepBelowChanged(); } void PlasmaWindow::Private::setMaximized(bool set) { if (maximized == set) { return; } maximized = set; emit q->maximizedChanged(); } void PlasmaWindow::Private::setMinimized(bool set) { if (minimized == set) { return; } minimized = set; emit q->minimizedChanged(); } void PlasmaWindow::Private::setOnAllDesktops(bool set) { if (onAllDesktops == set) { return; } onAllDesktops = set; emit q->onAllDesktopsChanged(); } void PlasmaWindow::Private::setDemandsAttention(bool set) { if (demandsAttention == set) { return; } demandsAttention = set; emit q->demandsAttentionChanged(); } void PlasmaWindow::Private::setCloseable(bool set) { if (closeable == set) { return; } closeable = set; emit q->closeableChanged(); } void PlasmaWindow::Private::setFullscreenable(bool set) { if (fullscreenable == set) { return; } fullscreenable = set; emit q->fullscreenableChanged(); } void PlasmaWindow::Private::setMaximizeable(bool set) { if (maximizeable == set) { return; } maximizeable = set; emit q->maximizeableChanged(); } void PlasmaWindow::Private::setMinimizeable(bool set) { if (minimizeable == set) { return; } minimizeable = set; emit q->minimizeableChanged(); } void PlasmaWindow::Private::setSkipTaskbar(bool skip) { if (skipTaskbar == skip) { return; } skipTaskbar = skip; emit q->skipTaskbarChanged(); } void PlasmaWindow::Private::setShadeable(bool set) { if (shadeable == set) { return; } shadeable = set; emit q->shadeableChanged(); } void PlasmaWindow::Private::setShaded(bool set) { if (shaded == set) { return; } shaded = set; emit q->shadedChanged(); } void PlasmaWindow::Private::setMovable(bool set) { if (movable == set) { return; } movable = set; emit q->movableChanged(); } void PlasmaWindow::Private::setResizable(bool set) { if (resizable == set) { return; } resizable = set; emit q->resizableChanged(); } void PlasmaWindow::Private::setVirtualDesktopChangeable(bool set) { if (virtualDesktopChangeable == set) { return; } virtualDesktopChangeable = set; emit q->virtualDesktopChangeableChanged(); } PlasmaWindow::Private::Private(org_kde_plasma_window *w, quint32 internalId, PlasmaWindow *q) : internalId(internalId) , q(q) { window.setup(w); org_kde_plasma_window_add_listener(w, &s_listener, this); } PlasmaWindow::PlasmaWindow(PlasmaWindowManagement *parent, org_kde_plasma_window *window, quint32 internalId) : QObject(parent) , d(new Private(window, internalId, this)) { } PlasmaWindow::~PlasmaWindow() { release(); } void PlasmaWindow::destroy() { d->window.destroy(); } void PlasmaWindow::release() { d->window.release(); } bool PlasmaWindow::isValid() const { return d->window.isValid(); } PlasmaWindow::operator org_kde_plasma_window*() const { return d->window; } PlasmaWindow::operator org_kde_plasma_window*() { return d->window; } QString PlasmaWindow::appId() const { return d->appId; } quint32 PlasmaWindow::pid() const { return d->pid; } QString PlasmaWindow::title() const { return d->title; } quint32 PlasmaWindow::virtualDesktop() const { return d->desktop; } bool PlasmaWindow::isActive() const { return d->active; } bool PlasmaWindow::isFullscreen() const { return d->fullscreen; } bool PlasmaWindow::isKeepAbove() const { return d->keepAbove; } bool PlasmaWindow::isKeepBelow() const { return d->keepBelow; } bool PlasmaWindow::isMaximized() const { return d->maximized; } bool PlasmaWindow::isMinimized() const { return d->minimized; } bool PlasmaWindow::isOnAllDesktops() const { return d->onAllDesktops; } bool PlasmaWindow::isDemandingAttention() const { return d->demandsAttention; } bool PlasmaWindow::isCloseable() const { return d->closeable; } bool PlasmaWindow::isFullscreenable() const { return d->fullscreenable; } bool PlasmaWindow::isMaximizeable() const { return d->maximizeable; } bool PlasmaWindow::isMinimizeable() const { return d->minimizeable; } bool PlasmaWindow::skipTaskbar() const { return d->skipTaskbar; } QIcon PlasmaWindow::icon() const { return d->icon; } bool PlasmaWindow::isShadeable() const { return d->shadeable; } bool PlasmaWindow::isShaded() const { return d->shaded; } bool PlasmaWindow::isResizable() const { return d->resizable; } bool PlasmaWindow::isMovable() const { return d->movable; } bool PlasmaWindow::isVirtualDesktopChangeable() const { return d->virtualDesktopChangeable; } void PlasmaWindow::requestActivate() { org_kde_plasma_window_set_state(d->window, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_ACTIVE); } void PlasmaWindow::requestClose() { org_kde_plasma_window_close(d->window); } void PlasmaWindow::requestMove() { org_kde_plasma_window_request_move(d->window); } void PlasmaWindow::requestResize() { org_kde_plasma_window_request_resize(d->window); } void PlasmaWindow::requestVirtualDesktop(quint32 desktop) { org_kde_plasma_window_set_virtual_desktop(d->window, desktop); } void PlasmaWindow::requestToggleMinimized() { if (d->minimized) { org_kde_plasma_window_set_state(d->window, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED, 0); } else { org_kde_plasma_window_set_state(d->window, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MINIMIZED); } } void PlasmaWindow::requestToggleMaximized() { if (d->maximized) { org_kde_plasma_window_set_state(d->window, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED, 0); } else { org_kde_plasma_window_set_state(d->window, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_MAXIMIZED); } } void PlasmaWindow::setMinimizedGeometry(Surface *panel, const QRect &geom) { org_kde_plasma_window_set_minimized_geometry(d->window, *panel, geom.x(), geom.y(), geom.width(), geom.height()); } void PlasmaWindow::unsetMinimizedGeometry(Surface *panel) { org_kde_plasma_window_unset_minimized_geometry(d->window, *panel); } void PlasmaWindow::requestToggleShaded() { if (d->shaded) { org_kde_plasma_window_set_state(d->window, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED, 0); } else { org_kde_plasma_window_set_state(d->window, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED, ORG_KDE_PLASMA_WINDOW_MANAGEMENT_STATE_SHADED); } } quint32 PlasmaWindow::internalId() const { return d->internalId; } QPointer PlasmaWindow::parentWindow() const { return d->parentWindow; } QRect PlasmaWindow::geometry() const { return d->geometry; } } } diff --git a/src/client/plasmawindowmanagement.h b/src/client/plasmawindowmanagement.h index d255613..7132d80 100644 --- a/src/client/plasmawindowmanagement.h +++ b/src/client/plasmawindowmanagement.h @@ -1,620 +1,614 @@ /******************************************************************** Copyright 2015 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #ifndef WAYLAND_PLASMAWINDOWMANAGEMENT_H #define WAYLAND_PLASMAWINDOWMANAGEMENT_H #include #include #include #include struct org_kde_plasma_window_management; struct org_kde_plasma_window; namespace KWayland { namespace Client { class EventQueue; class PlasmaWindow; class PlasmaWindowModel; class Surface; /** * @short Wrapper for the org_kde_plasma_window_management interface. * * PlasmaWindowManagement is a privileged interface. A Wayland compositor is allowed to ignore * any requests. The PlasmaWindowManagement allows to get information about the overall windowing * system. It allows to see which windows are currently available and thus is the base to implement * e.g. a task manager. * * This class provides a convenient wrapper for the org_kde_plasma_window_management interface. * It's main purpose is to create a PlasmaWindowManagementSurface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the Shell interface: * @code * PlasmaWindowManagement *s = registry->createPlasmaWindowManagement(name, version); * @endcode * * This creates the PlasmaWindowManagement and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * PlasmaWindowManagement *s = new PlasmaWindowManagement; * s->setup(registry->bindPlasmaWindowManagement(name, version)); * @endcode * * The PlasmaWindowManagement can be used as a drop-in replacement for any org_kde_plasma_window_management * pointer as it provides matching cast operators. * * @see Registry * @see PlasmaWindowManagementSurface **/ class KWAYLANDCLIENT_EXPORT PlasmaWindowManagement : public QObject { Q_OBJECT public: explicit PlasmaWindowManagement(QObject *parent = nullptr); virtual ~PlasmaWindowManagement(); /** * @returns @c true if managing a org_kde_plasma_window_management. **/ bool isValid() const; /** * Releases the org_kde_plasma_window_management interface. * After the interface has been released the PlasmaWindowManagement instance is no * longer valid and can be setup with another org_kde_plasma_window_management interface. * * Right before the interface is released the signal interfaceAboutToBeReleased is emitted. * @see interfaceAboutToBeReleased **/ void release(); /** * Destroys the data held by this PlasmaWindowManagement. * This method is supposed to be used when the connection to the Wayland * server goes away. Once the connection becomes invalid, 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_window_management interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, shell, &PlasmaWindowManagement::destroy); * @endcode * * Right before the data is destroyed, the signal interfaceAboutToBeDestroyed is emitted. * * @see release * @see interfaceAboutToBeDestroyed **/ void destroy(); /** * Setup this Shell to manage the @p shell. * When using Registry::createShell there is no need to call this * method. **/ void setup(org_kde_plasma_window_management *shell); /** * Sets the @p queue to use for creating a Surface. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for creating a Surface. **/ EventQueue *eventQueue(); operator org_kde_plasma_window_management*(); operator org_kde_plasma_window_management*() const; /** * Whether the system is currently showing the desktop. * This means that the system focuses on the desktop and hides other windows. * @see setShowingDesktop * @see showDesktop * @see hideDesktop * @see showingDesktopChanged **/ bool isShowingDesktop() const; /** * Requests to change the showing desktop state to @p show. * @see isShowingDesktop * @see showDesktop * @see hideDesktop **/ void setShowingDesktop(bool show); /** * Same as calling setShowingDesktop with @c true. * @see setShowingDesktop **/ void showDesktop(); /** * Same as calling setShowingDesktop with @c false. * @see setShowingDesktop **/ void hideDesktop(); /** * @returns All windows currently known to the PlasmaWindowManagement * @see windowCreated **/ QList windows() const; /** * @returns The currently active PlasmaWindow, the PlasmaWindow which * returns @c true in @link{PlasmaWindow::isActive} or @c nullptr in case * there is no active window. **/ PlasmaWindow *activeWindow() const; /** * Factory method to create a PlasmaWindowModel. * @returns a new created PlasmaWindowModel **/ PlasmaWindowModel *createWindowModel(); Q_SIGNALS: /** * This signal is emitted right before the interface is released. **/ void interfaceAboutToBeReleased(); /** * This signal is emitted right before the data is destroyed. **/ void interfaceAboutToBeDestroyed(); /** * The showing desktop state changed. * @see isShowingDesktop **/ void showingDesktopChanged(bool); /** * A new @p window got created. * @see windows **/ void windowCreated(KWayland::Client::PlasmaWindow *window); /** * The active window changed. * @see activeWindow **/ void activeWindowChanged(); /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the Compositor got created by * Registry::createPlasmaWindowManagement * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; /** * @short Wrapper for the org_kde_plasma_window interface. * * A PlasmaWindow gets created by the PlasmaWindowManagement and announced through * the @link{PlasmaWindowManagement::windowCreated} signal. The PlasmaWindow encapsulates * state about a window managed by the Wayland server and allows to request state changes. * * The PlasmaWindow will be automatically deleted when the PlasmaWindow gets unmapped. * * This class is a convenient wrapper for the org_kde_plasma_window interface. * The PlasmaWindow gets created by PlasmaWindowManagement. * * @see PlasmaWindowManager **/ class KWAYLANDCLIENT_EXPORT PlasmaWindow : public QObject { Q_OBJECT public: virtual ~PlasmaWindow(); /** * Releases the org_kde_plasma_window interface. * After the interface has been released the PlasmaWindow instance is no * longer valid and can be setup with another org_kde_plasma_window interface. **/ void release(); /** * Destroys the data held by this PlasmaWindow. * 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_window interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, source, &PlasmaWindow::destroy); * @endcode * * @see release **/ void destroy(); /** * @returns @c true if managing a org_kde_plasma_window. **/ bool isValid() const; operator org_kde_plasma_window*(); operator org_kde_plasma_window*() const; /** * @returns the window title. * @see titleChanged **/ QString title() const; /** * @returns the application id which should reflect the name of a desktop file. * @see appIdChanged **/ QString appId() const; /** * @returns the id of the virtual desktop this PlasmaWindow is on * @see virtualDesktopChanged **/ quint32 virtualDesktop() const; /** * @returns Whether the window is currently the active Window. * @see activeChanged **/ bool isActive() const; /** * @returns Whether the window is fullscreen * @see fullscreenChanged **/ bool isFullscreen() const; /** * @returns Whether the window is kept above other windows. * @see keepAboveChanged **/ bool isKeepAbove() const; /** * @returns Whether the window is kept below other window * @see keepBelowChanged **/ bool isKeepBelow() const; /** * @returns Whether the window is currently minimized * @see minimizedChanged **/ bool isMinimized() const; /** * @returns Whether the window is maximized. * @see maximizedChanged **/ bool isMaximized() const; /** * @returns Whether the window is shown on all desktops. * @see virtualDesktop * @see onAllDesktopsChanged **/ bool isOnAllDesktops() const; /** * @returns Whether the window is demanding attention. * @see demandsAttentionChanged **/ bool isDemandingAttention() const; /** * @returns Whether the window can be closed. * @see closeableChanged **/ bool isCloseable() const; /** * @returns Whether the window can be maximized. * @see maximizeableChanged **/ bool isMaximizeable() const; /** * @returns Whether the window can be minimized. * @see minimizeableChanged **/ bool isMinimizeable() const; /** * @returns Whether the window can be set to fullscreen. * @see fullscreenableChanged **/ bool isFullscreenable() const; /** * @returns Whether the window should be ignored by a task bar. * @see skipTaskbarChanged **/ bool skipTaskbar() const; /** * @returns The icon of the window. * @see iconChanged **/ QIcon icon() const; /** * @returns Whether the window can be set to the shaded state. * @see isShaded * @see shadeableChanged * @since 5.22 */ bool isShadeable() const; /** * @returns Whether the window is shaded, that is reduced to the window decoration * @see shadedChanged * @since 5.22 */ bool isShaded() const; /** * @returns Whether the window can be moved. * @see movableChanged * @since 5.22 */ bool isMovable() const; /** * @returns Whether the window can be resized. * @see resizableChanged * @since 5.22 */ bool isResizable() const; /** * @returns Whether the virtual desktop can be changed. * @see virtualDesktopChangeableChanged * @since 5.22 */ bool isVirtualDesktopChangeable() const; /** * @returns The process id this window belongs to. - * @see pidChanged + * or 0 if unset * @since 5.35 */ quint32 pid() const; /** * Requests to activate the window. **/ void requestActivate(); /** * Requests to close the window. **/ void requestClose(); /** * Requests to start an interactive window move operation. * @since 5.22 */ void requestMove(); /** * Requests to start an interactive resize operation. * @since 5.22 */ void requestResize(); /** * Requests to send the window to virtual @p desktop. **/ void requestVirtualDesktop(quint32 desktop); /** * Requests the window at this model row index have its minimized state toggled. */ void requestToggleMinimized(); /** * Requests the window at this model row index have its maximized state toggled. */ void requestToggleMaximized(); /** * Sets the geometry of the taskbar entry for this window * relative to a panel in particular * @since 5.5 */ void setMinimizedGeometry(Surface *panel, const QRect &geom); /** * Remove the task geometry information for a particular panel * @since 5.5 */ void unsetMinimizedGeometry(Surface *panel); /** * Requests the window at this model row index have its shaded state toggled. * @since 5.22 */ void requestToggleShaded(); /** * An internal window identifier. * This is not a global window identifier. * This identifier does not correspond to QWindow::winId in any way. **/ quint32 internalId() const; /** * The parent window of this PlasmaWindow. * * If there is a parent window, this window is a transient window for the * parent window. If this method returns a null PlasmaWindow it means this * window is a top level window and is not a transient window. * * @see parentWindowChanged * @since 5.24 **/ QPointer parentWindow() const; /** * @returns The window geometry in absolute coordinates. * @see geometryChanged * @since 5.25 **/ QRect geometry() const; Q_SIGNALS: /** * The window title changed. * @see title **/ void titleChanged(); /** * The application id changed. * @see appId **/ void appIdChanged(); - /** - * The process id changed. - * @see pid - * @since 5.35 - **/ - void pidChanged(); /** * The virtual desktop changed. * @see virtualDesktop **/ void virtualDesktopChanged(); /** * The window became active or inactive. * @see isActive **/ void activeChanged(); /** * The fullscreen state changed. * @see isFullscreen **/ void fullscreenChanged(); /** * The keep above state changed. * @see isKeepAbove **/ void keepAboveChanged(); /** * The keep below state changed. * @see isKeepBelow **/ void keepBelowChanged(); /** * The minimized state changed. * @see isMinimized **/ void minimizedChanged(); /** * The maximized state changed. * @see isMaximized **/ void maximizedChanged(); /** * The on all desktops state changed. * @see isOnAllDesktops **/ void onAllDesktopsChanged(); /** * The demands attention state changed. * @see isDemandingAttention **/ void demandsAttentionChanged(); /** * The closeable state changed. * @see isCloseable **/ void closeableChanged(); /** * The minimizeable state changed. * @see isMinimizeable **/ void minimizeableChanged(); /** * The maximizeable state changed. * @see isMaximizeable **/ void maximizeableChanged(); /** * The fullscreenable state changed. * @see isFullscreenable **/ void fullscreenableChanged(); /** * The skip taskbar state changed. * @see skipTaskbar **/ void skipTaskbarChanged(); /** * The window icon changed. * @see icon **/ void iconChanged(); /** * The shadeable state changed. * @see isShadeable * @since 5.22 */ void shadeableChanged(); /** * The shaded state changed. * @see isShaded * @since 5.22 */ void shadedChanged(); /** * The movable state changed. * @see isMovable * @since 5.22 */ void movableChanged(); /** * The resizable state changed. * @see isResizable * @since 5.22 */ void resizableChanged(); /** * The virtual desktop changeable state changed. * @see virtualDesktopChangeable * @since 5.22 */ void virtualDesktopChangeableChanged(); /** * The window got unmapped and is no longer available to the Wayland server. * This instance will be automatically deleted and one should connect to this * signal to perform cleanup. **/ void unmapped(); /** * This signal is emitted whenever the parent window changes. * @see parentWindow * @since 5.24 **/ void parentWindowChanged(); /** * This signal is emitted whenever the window geometry changes. * @see geometry * @since 5.25 **/ void geometryChanged(); private: friend class PlasmaWindowManagement; explicit PlasmaWindow(PlasmaWindowManagement *parent, org_kde_plasma_window *dataOffer, quint32 internalId); class Private; QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Client::PlasmaWindow*) #endif diff --git a/src/client/protocols/plasma-window-management.xml b/src/client/protocols/plasma-window-management.xml index 3129c59..dfac24c 100644 --- a/src/client/protocols/plasma-window-management.xml +++ b/src/client/protocols/plasma-window-management.xml @@ -1,271 +1,270 @@ . ]]> This interface manages application windows. It provides requests to show and hide the desktop and emits an event every time a window is created so that the client can use it to manage the window. Only one client can bind this interface at a time. Tell the compositor to show/hide the desktop. This event will be sent whenever the show desktop mode changes. E.g. when it is entered or left. On binding the interface the current state is sent. This event will be sent immediately after a window is mapped. Manages and control an application window. Only one client can bind this interface at a time. Set window state. Values for state argument are described by org_kde_plasma_window_management.state and can be used together in a bitfield. The flags bitfield describes which flags are supposed to be set, the state bitfield the value for the set flags Maps the window to a different virtual desktop. To show the window on all virtual desktops, call the org_kde_plasma_window.set_state request and specify a on_all_desktops state in the bitfield. Sets the geometry of the taskbar entry for this window. The geometry is relative to a panel in particular. Remove the task geometry information for a particular panel. Close this window. Request an interactive move for this window. Request an interactive resize for this window. Removes the resource bound for this org_kde_plasma_window. The compositor will write the window icon into the provided file descriptor. The data is a serialized QIcon with QDataStream. This event will be sent as soon as the window title is changed. This event will be sent as soon as the application identifier is changed. This event will be sent as soon as the window state changes. Values for state argument are described by org_kde_plasma_window_management.state. This event will be sent when a window is moved to another virtual desktop. It is not sent if it becomes visible on all virtual desktops though. This event will be sent whenever the themed icon name changes. May be null. This event will be sent immediately after the window is closed and its surface is unmapped. This event will be sent immediately after all initial state been sent to the client. If the Plasma window is already unmapped, the unmapped event will be sent before the initial_state event. This event will be sent whenever the parent window of this org_kde_plasma_window changes. The passed parent is another org_kde_plasma_window and this org_kde_plasma_window is a transient window to the parent window. If the parent argument is null, this org_kde_plasma_window does not have a parent window. This event will be sent whenever the window geometry of this org_kde_plasma_window changes. The coordinages are in absolute coordinates of the windowing system. This event will be sent whenever the icon of the window changes, but there is no themed icon name. Common examples are Xwayland windows which have a pixmap based icon. The client can request the icon using get_icon. This event will be sent when the compositor has set the process id this window belongs to. - The pid will initially be 0, meaning it has not been set. As the compositor sets it, the client - gets notified. + This should be set once before the initial_state is sent.