mirror of https://github.com/cutefishos/statusbar
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
219 lines
5.9 KiB
C++
219 lines
5.9 KiB
C++
/*
|
|
* Copyright (C) 2021 CutefishOS Team.
|
|
*
|
|
* Author: cutefishos <cutefishos@foxmail.com>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* any later version.
|
|
*
|
|
* This program 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "capplications.h"
|
|
|
|
#include <QRegularExpression>
|
|
#include <QSettings>
|
|
#include <QLocale>
|
|
|
|
#include <QDirIterator>
|
|
#include <QDir>
|
|
|
|
static CApplications *SELF = nullptr;
|
|
static QString s_systemAppFolder = "/usr/share/applications";
|
|
|
|
static QByteArray detectDesktopEnvironment()
|
|
{
|
|
const QByteArray desktop = qgetenv("XDG_CURRENT_DESKTOP");
|
|
|
|
if (!desktop.isEmpty())
|
|
return desktop.toUpper();
|
|
|
|
return QByteArray("UNKNOWN");
|
|
}
|
|
|
|
CApplications *CApplications::self()
|
|
{
|
|
if (!SELF)
|
|
SELF = new CApplications;
|
|
|
|
return SELF;
|
|
}
|
|
|
|
CApplications::CApplications(QObject *parent)
|
|
: QObject(parent)
|
|
, m_watcher(new QFileSystemWatcher(this))
|
|
{
|
|
m_watcher->addPath(s_systemAppFolder);
|
|
connect(m_watcher, &QFileSystemWatcher::directoryChanged, this, &CApplications::refresh);
|
|
refresh();
|
|
}
|
|
|
|
CApplications::~CApplications()
|
|
{
|
|
while (!m_items.isEmpty())
|
|
delete m_items.takeFirst();
|
|
}
|
|
|
|
CAppItem *CApplications::find(const QString &fileName)
|
|
{
|
|
for (CAppItem *item : m_items)
|
|
if (item->path == fileName)
|
|
return item;
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
CAppItem *CApplications::matchItem(quint32 pid)
|
|
{
|
|
QStringList commands = commandFromPid(pid);
|
|
|
|
// The value returned from the commandFromPid() may be empty.
|
|
// Calling first() and last() below will cause the statusbar to crash.
|
|
if (commands.isEmpty())
|
|
return nullptr;
|
|
|
|
QString command = commands.first();
|
|
QString commandName = commands.last();
|
|
|
|
if (command.isEmpty())
|
|
return nullptr;
|
|
|
|
for (CAppItem *item : m_items) {
|
|
if (item->fullExec == command ||
|
|
item->exec == command ||
|
|
item->fullExec == commandName ||
|
|
item->exec == commandName) {
|
|
return item;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void CApplications::refresh()
|
|
{
|
|
QStringList addedEntries;
|
|
for (CAppItem *item : m_items)
|
|
addedEntries.append(item->path);
|
|
|
|
QStringList allEntries;
|
|
QDirIterator it(s_systemAppFolder, { "*.desktop" }, QDir::NoFilter, QDirIterator::Subdirectories);
|
|
|
|
while (it.hasNext()) {
|
|
const QString &filePath = it.next();
|
|
|
|
if (!QFile::exists(filePath))
|
|
continue;
|
|
|
|
allEntries.append(filePath);
|
|
}
|
|
|
|
for (const QString &filePath : allEntries) {
|
|
if (!addedEntries.contains(filePath)) {
|
|
addApplication(filePath);
|
|
}
|
|
}
|
|
|
|
for (CAppItem *item : m_items) {
|
|
if (!allEntries.contains(item->path)) {
|
|
removeApplication(item);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CApplications::addApplication(const QString &filePath)
|
|
{
|
|
if (find(filePath))
|
|
return;
|
|
|
|
QSettings desktop(filePath, QSettings::IniFormat);
|
|
desktop.setIniCodec("UTF-8");
|
|
desktop.beginGroup("Desktop Entry");
|
|
|
|
// Skip...
|
|
if (desktop.contains("OnlyShowIn")) {
|
|
const QString &value = desktop.value("OnlyShowIn").toString();
|
|
if (!value.contains(detectDesktopEnvironment(), Qt::CaseInsensitive)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (desktop.value("NoDisplay").toBool() ||
|
|
desktop.value("Hidden").toBool()) {
|
|
return;
|
|
}
|
|
|
|
// Local Name
|
|
QString localName = desktop.value(QString("Name[%1]").arg(QLocale::system().name())).toString();
|
|
if (localName.isEmpty())
|
|
localName = desktop.value("Name").toString();
|
|
|
|
// Exec
|
|
QString simplifiedExec = desktop.value("Exec").toString();
|
|
simplifiedExec.remove(QRegularExpression("%."));
|
|
simplifiedExec.remove(QRegularExpression("^\""));
|
|
// appExec.remove(QRegularExpression(" *$"));
|
|
simplifiedExec = simplifiedExec.simplified();
|
|
|
|
// New data
|
|
CAppItem *item = new CAppItem;
|
|
item->path = filePath;
|
|
item->localName = localName;
|
|
item->name = desktop.value("Name").toString();;
|
|
item->comment = desktop.value("Comment").toString();
|
|
item->icon = desktop.value("Icon").toString();
|
|
item->fullExec = desktop.value("Exec").toString();
|
|
item->exec = simplifiedExec;
|
|
m_items.append(item);
|
|
}
|
|
|
|
void CApplications::removeApplication(CAppItem *item)
|
|
{
|
|
m_items.removeOne(item);
|
|
delete item;
|
|
}
|
|
|
|
QStringList CApplications::commandFromPid(quint32 pid)
|
|
{
|
|
QFile file(QString("/proc/%1/cmdline").arg(pid));
|
|
|
|
if (file.open(QIODevice::ReadOnly)) {
|
|
QByteArray cmd = file.readAll();
|
|
|
|
// ref: https://github.com/KDE/kcoreaddons/blob/230c98aa7e01f9e36a9c2776f3633182e6778002/src/lib/util/kprocesslist_unix.cpp#L137
|
|
if (!cmd.isEmpty()) {
|
|
// extract non-truncated name from cmdline
|
|
int zeroIndex = cmd.indexOf('\0');
|
|
int processNameStart = cmd.lastIndexOf('/', zeroIndex);
|
|
if (processNameStart == -1) {
|
|
processNameStart = 0;
|
|
} else {
|
|
processNameStart++;
|
|
}
|
|
|
|
QString name = QString::fromLocal8Bit(cmd.mid(processNameStart, zeroIndex - processNameStart));
|
|
|
|
cmd.replace('\0', ' ');
|
|
QString command = QString::fromLocal8Bit(cmd).trimmed();
|
|
|
|
// There may be parameters.
|
|
if (command.split(' ').size() > 1) {
|
|
command = command.split(' ').first();
|
|
}
|
|
|
|
return { command, name };
|
|
}
|
|
}
|
|
|
|
return QStringList();
|
|
}
|
|
|