diff --git a/autotests/client/test_plasma_window_model.cpp b/autotests/client/test_plasma_window_model.cpp --- a/autotests/client/test_plasma_window_model.cpp +++ b/autotests/client/test_plasma_window_model.cpp @@ -79,6 +79,7 @@ void testGeometry(); void testTitle(); void testAppId(); + void testPid(); void testVirtualDesktop(); // TODO icon: can we ensure a theme is installed on CI? void testRequests(); @@ -220,6 +221,7 @@ 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"); @@ -332,6 +334,7 @@ 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() @@ -521,6 +524,31 @@ 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); + 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::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(); diff --git a/autotests/client/test_wayland_windowmanagement.cpp b/autotests/client/test_wayland_windowmanagement.cpp --- a/autotests/client/test_wayland_windowmanagement.cpp +++ b/autotests/client/test_wayland_windowmanagement.cpp @@ -67,6 +67,7 @@ void testParentWindow(); void testGeometry(); void testIcon(); + void testPid(); void cleanup(); @@ -581,5 +582,24 @@ 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); +} + QTEST_MAIN(TestWindowManagement) #include "test_wayland_windowmanagement.moc" diff --git a/src/client/plasmawindowmanagement.h b/src/client/plasmawindowmanagement.h --- a/src/client/plasmawindowmanagement.h +++ b/src/client/plasmawindowmanagement.h @@ -380,6 +380,12 @@ * @since 5.22 */ bool isVirtualDesktopChangeable() const; + /** + * @returns The process id this window belongs to. + * @see pidChanged + * @since 5.35 + */ + quint32 pid() const; /** * Requests to activate the window. @@ -470,6 +476,12 @@ **/ void appIdChanged(); /** + * The process id changed. + * @see pid + * @since 5.35 + **/ + void pidChanged(); + /** * The virtual desktop changed. * @see virtualDesktop **/ diff --git a/src/client/plasmawindowmanagement.cpp b/src/client/plasmawindowmanagement.cpp --- a/src/client/plasmawindowmanagement.cpp +++ b/src/client/plasmawindowmanagement.cpp @@ -93,10 +93,12 @@ 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); @@ -124,6 +126,7 @@ 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); @@ -341,7 +344,8 @@ initialStateCallback, parentWindowCallback, windowGeometryCallback, - iconChangedCallback + iconChangedCallback, + pidChangedCallback }; void PlasmaWindow::Private::parentWindowCallback(void *data, org_kde_plasma_window *window, org_kde_plasma_window *parent) @@ -422,6 +426,17 @@ 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) @@ -749,6 +764,11 @@ return d->appId; } +quint32 PlasmaWindow::pid() const +{ + return d->pid; +} + QString PlasmaWindow::title() const { return d->title; diff --git a/src/client/plasmawindowmodel.h b/src/client/plasmawindowmodel.h --- a/src/client/plasmawindowmodel.h +++ b/src/client/plasmawindowmodel.h @@ -101,7 +101,11 @@ /** * @since 5.25 */ - Geometry + Geometry, + /** + * @since 5.35 + */ + Pid }; Q_ENUM(AdditionalRoles) diff --git a/src/client/plasmawindowmodel.cpp b/src/client/plasmawindowmodel.cpp --- a/src/client/plasmawindowmodel.cpp +++ b/src/client/plasmawindowmodel.cpp @@ -225,6 +225,8 @@ return window->icon(); } else if (role == AppId) { return window->appId(); + } else if (role == Pid) { + return window->pid(); } else if (role == IsActive) { return window->isActive(); } else if (role == IsFullscreenable) { diff --git a/src/client/protocols/plasma-window-management.xml b/src/client/protocols/plasma-window-management.xml --- a/src/client/protocols/plasma-window-management.xml +++ b/src/client/protocols/plasma-window-management.xml @@ -17,7 +17,7 @@ along with this program. If not, see . ]]> - + This interface manages application windows. It provides requests to show and hide the desktop and emits @@ -257,5 +257,15 @@ 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. + + + + diff --git a/src/server/plasmawindowmanagement_interface.h b/src/server/plasmawindowmanagement_interface.h --- a/src/server/plasmawindowmanagement_interface.h +++ b/src/server/plasmawindowmanagement_interface.h @@ -87,6 +87,7 @@ void setTitle(const QString &title); void setAppId(const QString &appId); + void setPid(quint32 pid); void setVirtualDesktop(quint32 desktop); void setActive(bool set); void setMinimized(bool set); diff --git a/src/server/plasmawindowmanagement_interface.cpp b/src/server/plasmawindowmanagement_interface.cpp --- a/src/server/plasmawindowmanagement_interface.cpp +++ b/src/server/plasmawindowmanagement_interface.cpp @@ -72,6 +72,7 @@ void createResource(wl_resource *parent, uint32_t id); void setTitle(const QString &title); void setAppId(const QString &appId); + void setPid(quint32 pid); void setThemedIconName(const QString &iconName); void setIcon(const QIcon &icon); void setVirtualDesktop(quint32 desktop); @@ -109,6 +110,7 @@ PlasmaWindowInterface *q; QString m_title; QString m_appId; + quint32 m_pid = 0; QString m_themedIconName; QIcon m_icon; quint32 m_virtualDesktop = 0; @@ -329,6 +331,9 @@ if (!m_appId.isEmpty()) { org_kde_plasma_window_send_app_id_changed(resource, m_appId.toUtf8().constData()); } + if (m_pid != 0) { + org_kde_plasma_window_send_pid_changed(resource, m_pid); + } if (!m_title.isEmpty()) { org_kde_plasma_window_send_title_changed(resource, m_title.toUtf8().constData()); } @@ -369,6 +374,17 @@ } } +void PlasmaWindowInterface::Private::setPid(quint32 pid) +{ + if (m_pid == pid) { + return; + } + m_pid = pid; + for (auto it = resources.constBegin(); it != resources.constEnd(); ++it) { + org_kde_plasma_window_send_pid_changed(*it, pid); + } +} + void PlasmaWindowInterface::Private::setThemedIconName(const QString &iconName) { if (m_themedIconName == iconName) { @@ -655,6 +671,11 @@ d->setAppId(appId); } +void PlasmaWindowInterface::setPid(quint32 pid) +{ + d->setPid(pid); +} + void PlasmaWindowInterface::setTitle(const QString &title) { d->setTitle(title);