File: utils.cpp

package info (click to toggle)
plasma-workspace 4%3A6.5.4-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 99,452 kB
  • sloc: cpp: 125,486; python: 4,246; xml: 2,449; perl: 572; sh: 230; javascript: 75; ruby: 39; ansic: 13; makefile: 9
file content (132 lines) | stat: -rw-r--r-- 4,310 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/*
    SPDX-FileCopyrightText: 2019 Kai Uwe Broulik <kde@privat.broulik.de>

    SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/

#include "utils_p.h"

#include <ranges>

#include "notifications.h"

#include <QAbstractItemModel>
#include <QAbstractProxyModel>
#include <QConcatenateTablesProxyModel>
#include <QCoreApplication>
#include <QDBusConnection>
#include <QDBusConnectionInterface>
#include <QFile>
#include <QFileInfo>
#include <QMetaEnum>
#include <QSettings>
#include <QTextStream>

#include <KProcessList>

using namespace NotificationManager;

QHash<int, QByteArray> Utils::roleNames()
{
    static QHash<int, QByteArray> s_roles;

    if (s_roles.isEmpty()) {
        // This generates role names from the Roles enum in the form of: FooRole -> foo
        const QMetaEnum e = QMetaEnum::fromType<Notifications::Roles>();

        // Qt built-in roles we use
        s_roles.insert(Qt::DisplayRole, QByteArrayLiteral("display"));
        s_roles.insert(Qt::DecorationRole, QByteArrayLiteral("decoration"));
        s_roles.insert(Qt::AccessibleDescriptionRole, QByteArrayLiteral("accessibleDescription"));

        for (int i = 0; i < e.keyCount(); ++i) {
            const int value = e.value(i);

            QByteArray key(e.key(i));
            key[0] = key[0] + 32; // lower case first letter
            key.chop(4); // strip "Role" suffix

            s_roles.insert(value, key);
        }

        s_roles.insert(Notifications::IdRole, QByteArrayLiteral("notificationId")); // id is QML-reserved
    }

    return s_roles;
}

QString Utils::processNameFromPid(uint pid)
{
    auto processInfo = KProcessList::processInfo(pid);

    if (!processInfo.isValid()) {
        return QString();
    }

    return processInfo.name();
}

QString Utils::desktopEntryFromPid(uint pid)
{
    const QString flatpakInfoPath = QStringLiteral("/proc/%1/root/.flatpak-info").arg(QString::number(pid));
    if (QFileInfo::exists(flatpakInfoPath)) {
        QSettings flatpakInfo(flatpakInfoPath, QSettings::IniFormat);

        const QString name = flatpakInfo.value("Application/name").toString();
        if (!name.isEmpty()) {
            return name;
        }

        // If it's a flatpak, can't be a snap, bail out.
        return QString();
    }

    QFile environFile(QStringLiteral("/proc/%1/environ").arg(QString::number(pid)));
    if (environFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
        constexpr QByteArrayView bamfDesktopFileHint("BAMF_DESKTOP_FILE_HINT");

        const QByteArray environ = environFile.readAll();
#if (defined(__GNUC__) && __GNUC__ >= 12) || !defined(__GNUC__)
        for (const QByteArrayView line : QByteArrayView(environ) | std::views::split('\0')) {
#else
        for (const QByteArrayView line : environ.split('\0')) {
#endif
            const auto equalsIdx = line.indexOf('=');
            if (equalsIdx == -1) {
                continue;
            }

            const QByteArrayView key = line.sliced(0, equalsIdx);
            if (key == bamfDesktopFileHint) {
                return QString::fromUtf8(line.sliced(equalsIdx + 1));
            }
        }
    }

    return QString();
}

QModelIndex Utils::mapToModel(const QModelIndex &idx, const QAbstractItemModel *sourceModel)
{
    // KModelIndexProxyMapper can only map different indices to a single source
    // but we have the other way round, a single index that splits into different source models
    QModelIndex resolvedIdx = idx;
    while (resolvedIdx.isValid() && resolvedIdx.model() != sourceModel) {
        if (auto *proxyModel = qobject_cast<const QAbstractProxyModel *>(resolvedIdx.model())) {
            resolvedIdx = proxyModel->mapToSource(resolvedIdx);
            // QConcatenateTablesProxyModel isn't a "real" proxy model, so we need to special case for it :(
        } else if (auto *concatenateModel = qobject_cast<const QConcatenateTablesProxyModel *>(resolvedIdx.model())) {
            resolvedIdx = concatenateModel->mapToSource(resolvedIdx);
        } else {
            if (resolvedIdx.model() != sourceModel) {
                resolvedIdx = QModelIndex(); // give up
            }
        }
    }
    return resolvedIdx;
}

bool Utils::isDBusMaster()
{
    return qApp->property("_plasma_dbus_master").toBool();
}