2023-07-10 10:18:33 +08:00
|
|
|
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
|
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
|
|
|
|
2023-08-07 14:25:22 +08:00
|
|
|
#include "dbus/applicationservice.h"
|
2023-08-21 16:02:26 +08:00
|
|
|
#include "APPobjectmanager1adaptor.h"
|
2023-09-05 11:49:28 +08:00
|
|
|
#include "applicationchecker.h"
|
2023-09-13 11:47:04 +08:00
|
|
|
#include "applicationmanagerstorage.h"
|
2023-09-08 10:58:41 +08:00
|
|
|
#include "propertiesForwarder.h"
|
2023-08-07 14:25:22 +08:00
|
|
|
#include "dbus/instanceadaptor.h"
|
2023-08-30 18:36:52 +08:00
|
|
|
#include "launchoptions.h"
|
2023-10-19 14:18:44 +08:00
|
|
|
#include "desktopentry.h"
|
2023-07-17 14:49:35 +08:00
|
|
|
#include <QUuid>
|
2023-07-24 14:12:59 +08:00
|
|
|
#include <QStringList>
|
|
|
|
#include <QList>
|
|
|
|
#include <QUrl>
|
|
|
|
#include <QRegularExpression>
|
|
|
|
#include <QProcess>
|
2023-09-01 15:48:29 +08:00
|
|
|
#include <QStandardPaths>
|
2023-07-24 14:12:59 +08:00
|
|
|
#include <algorithm>
|
2023-08-20 18:25:59 +08:00
|
|
|
#include <new>
|
2023-09-13 11:47:04 +08:00
|
|
|
#include <utility>
|
2023-09-04 15:57:59 +08:00
|
|
|
#include <wordexp.h>
|
2023-08-20 18:25:59 +08:00
|
|
|
|
2023-09-13 11:47:04 +08:00
|
|
|
ApplicationService::ApplicationService(DesktopFile source,
|
|
|
|
ApplicationManager1Service *parent,
|
|
|
|
std::weak_ptr<ApplicationManager1Storage> storage)
|
2023-08-20 18:25:59 +08:00
|
|
|
: QObject(parent)
|
2023-10-08 17:18:06 +08:00
|
|
|
, m_scaleFactor(getScaleFactor())
|
2023-09-13 11:47:04 +08:00
|
|
|
, m_storage(std::move(storage))
|
2023-08-20 18:25:59 +08:00
|
|
|
, m_desktopSource(std::move(source))
|
|
|
|
{
|
2023-09-14 17:16:10 +08:00
|
|
|
auto storagePtr = m_storage.lock();
|
|
|
|
if (!storagePtr) {
|
|
|
|
m_lastLaunch = -1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto appId = id();
|
|
|
|
auto value = storagePtr->readApplicationValue(appId, ApplicationPropertiesGroup, LastLaunchedTime);
|
|
|
|
if (value.isNull()) {
|
|
|
|
if (!storagePtr->createApplicationValue(
|
|
|
|
appId, ApplicationPropertiesGroup, LastLaunchedTime, QVariant::fromValue<qint64>(0))) {
|
|
|
|
m_lastLaunch = -1;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
m_lastLaunch = value.toInt();
|
2023-10-08 17:18:06 +08:00
|
|
|
|
|
|
|
value = storagePtr->readApplicationValue(appId, ApplicationPropertiesGroup, ScaleFactor);
|
|
|
|
if (!value.isNull()) {
|
|
|
|
bool ok{false};
|
|
|
|
auto tmp = value.toDouble(&ok);
|
|
|
|
if (ok) {
|
|
|
|
m_scaleFactor = tmp;
|
|
|
|
m_customScale = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!QDBusConnection::sessionBus().connect("org.deepin.dde.XSettings1",
|
|
|
|
"/org/deepin/dde/XSettings1",
|
|
|
|
"org.deepin.dde.XSettings1",
|
|
|
|
"SetScaleFactorDone",
|
|
|
|
this,
|
|
|
|
SLOT(onGlobalScaleFactorChanged()))) {
|
|
|
|
qWarning() << "connect to org.deepin.dde.XSettings1 failed, scaleFactor is invalid.";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ApplicationService::onGlobalScaleFactorChanged() noexcept
|
|
|
|
{
|
|
|
|
if (!m_customScale) {
|
|
|
|
m_scaleFactor = getScaleFactor();
|
|
|
|
}
|
2023-08-20 18:25:59 +08:00
|
|
|
}
|
2023-07-10 10:18:33 +08:00
|
|
|
|
2023-07-24 14:12:59 +08:00
|
|
|
ApplicationService::~ApplicationService()
|
2023-07-10 10:18:33 +08:00
|
|
|
{
|
2023-09-14 17:16:10 +08:00
|
|
|
detachAllInstance();
|
2023-07-10 10:18:33 +08:00
|
|
|
}
|
|
|
|
|
2023-09-13 11:47:04 +08:00
|
|
|
QSharedPointer<ApplicationService> ApplicationService::createApplicationService(
|
|
|
|
DesktopFile source, ApplicationManager1Service *parent, std::weak_ptr<ApplicationManager1Storage> storage) noexcept
|
2023-08-20 18:25:59 +08:00
|
|
|
{
|
2023-09-13 11:47:04 +08:00
|
|
|
QSharedPointer<ApplicationService> app{new (std::nothrow) ApplicationService{std::move(source), parent, std::move(storage)}};
|
2023-08-21 16:02:26 +08:00
|
|
|
if (!app) {
|
|
|
|
qCritical() << "new application service failed.";
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2023-08-20 18:25:59 +08:00
|
|
|
QString objectPath;
|
|
|
|
QTextStream sourceStream;
|
|
|
|
|
2023-08-21 16:02:26 +08:00
|
|
|
auto appId = app->desktopFileSource().desktopId();
|
|
|
|
|
|
|
|
if (!appId.isEmpty()) {
|
|
|
|
objectPath = QString{DDEApplicationManager1ObjectPath} + "/" + escapeToObjectPath(appId);
|
2023-08-20 18:25:59 +08:00
|
|
|
} else {
|
|
|
|
objectPath = QString{DDEApplicationManager1ObjectPath} + "/" + QUuid::createUuid().toString(QUuid::Id128);
|
|
|
|
}
|
2023-08-21 16:02:26 +08:00
|
|
|
|
2023-08-23 17:20:47 +08:00
|
|
|
DesktopFileGuard guard{app->desktopFileSource()};
|
|
|
|
|
|
|
|
if (!guard.try_open()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2023-08-21 16:02:26 +08:00
|
|
|
sourceStream.setDevice(app->desktopFileSource().sourceFile());
|
2023-08-20 18:25:59 +08:00
|
|
|
std::unique_ptr<DesktopEntry> entry{std::make_unique<DesktopEntry>()};
|
|
|
|
auto error = entry->parse(sourceStream);
|
|
|
|
|
2023-09-20 18:29:42 +08:00
|
|
|
if (error != ParserError::NoError) {
|
2023-08-25 10:47:17 +08:00
|
|
|
qWarning() << "parse failed:" << error << app->desktopFileSource().sourcePath();
|
|
|
|
return nullptr;
|
2023-08-20 18:25:59 +08:00
|
|
|
}
|
|
|
|
|
2023-09-05 11:49:28 +08:00
|
|
|
if (!shouldBeShown(entry)) {
|
|
|
|
qDebug() << "application shouldn't be shown:" << app->desktopFileSource().sourcePath();
|
|
|
|
return nullptr;
|
2023-08-20 18:25:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
app->m_entry.reset(entry.release());
|
|
|
|
app->m_applicationPath = QDBusObjectPath{std::move(objectPath)};
|
|
|
|
|
|
|
|
// TODO: icon lookup
|
2023-08-21 16:02:26 +08:00
|
|
|
if (auto *ptr = new (std::nothrow) APPObjectManagerAdaptor{app.data()}; ptr == nullptr) {
|
|
|
|
qCritical() << "new Object Manager of Application failed.";
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2023-09-08 10:58:41 +08:00
|
|
|
if (auto *ptr = new (std::nothrow) PropertiesForwarder{app->m_applicationPath.path(), app.data()}; ptr == nullptr) {
|
|
|
|
qCritical() << "new PropertiesForwarder of Application failed.";
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2023-08-20 18:25:59 +08:00
|
|
|
return app;
|
|
|
|
}
|
|
|
|
|
2023-09-05 11:49:28 +08:00
|
|
|
bool ApplicationService::shouldBeShown(const std::unique_ptr<DesktopEntry> &entry) noexcept
|
|
|
|
{
|
2023-09-06 14:19:50 +08:00
|
|
|
if (ApplicationFilter::hiddenCheck(entry)) {
|
|
|
|
return false;
|
2023-09-05 11:49:28 +08:00
|
|
|
}
|
|
|
|
|
2023-09-06 14:19:50 +08:00
|
|
|
if (ApplicationFilter::tryExecCheck(entry)) {
|
|
|
|
return false;
|
2023-09-05 11:49:28 +08:00
|
|
|
}
|
|
|
|
|
2023-09-06 14:19:50 +08:00
|
|
|
if (ApplicationFilter::showInCheck(entry)) {
|
|
|
|
return false;
|
2023-09-05 11:49:28 +08:00
|
|
|
}
|
|
|
|
|
2023-09-06 14:19:50 +08:00
|
|
|
return true;
|
2023-09-05 11:49:28 +08:00
|
|
|
}
|
|
|
|
|
2023-08-20 18:25:59 +08:00
|
|
|
QDBusObjectPath ApplicationService::Launch(const QString &action, const QStringList &fields, const QVariantMap &options)
|
2023-07-17 14:49:35 +08:00
|
|
|
{
|
2023-07-24 14:12:59 +08:00
|
|
|
QString execStr;
|
|
|
|
bool ok;
|
|
|
|
const auto &supportedActions = actions();
|
2023-09-07 15:55:25 +08:00
|
|
|
auto optionsMap = options;
|
|
|
|
QString oldEnv;
|
|
|
|
|
|
|
|
auto factor = getDeepinWineScaleFactor(m_desktopSource.desktopId()).toDouble();
|
|
|
|
if (factor != 1.0) {
|
|
|
|
if (auto it = optionsMap.find("env"); it != optionsMap.cend()) {
|
|
|
|
oldEnv = it->value<QString>();
|
|
|
|
}
|
|
|
|
oldEnv.append(QString{"DEEPIN_WINE_SCALE=%1;"}.arg(factor));
|
|
|
|
optionsMap.insert("env", oldEnv);
|
|
|
|
}
|
2023-07-24 14:12:59 +08:00
|
|
|
|
|
|
|
while (!action.isEmpty() and !supportedActions.isEmpty()) { // break trick
|
|
|
|
if (auto index = supportedActions.indexOf(action); index == -1) {
|
|
|
|
qWarning() << "can't find " << action << " in supported actions List. application will use default action to launch.";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2023-08-31 16:25:21 +08:00
|
|
|
const auto &actionHeader = QString{"%1%2"}.arg(DesktopFileActionKey, action);
|
2023-07-24 14:12:59 +08:00
|
|
|
const auto &actionExec = m_entry->value(actionHeader, "Exec");
|
|
|
|
if (!actionExec) {
|
|
|
|
break;
|
|
|
|
}
|
2023-10-12 18:05:00 +08:00
|
|
|
|
|
|
|
execStr = toString(actionExec.value());
|
|
|
|
if (execStr.isEmpty()) {
|
|
|
|
qWarning() << "exec value to string failed, try default action."; // we need this log.
|
2023-07-24 14:12:59 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
2023-07-17 14:49:35 +08:00
|
|
|
}
|
2023-07-24 14:12:59 +08:00
|
|
|
|
|
|
|
if (execStr.isEmpty()) {
|
|
|
|
auto Actions = m_entry->value(DesktopFileEntryKey, "Exec");
|
|
|
|
if (!Actions) {
|
2023-09-01 17:30:14 +08:00
|
|
|
QString msg{"application can't be executed."};
|
|
|
|
qWarning() << msg;
|
|
|
|
sendErrorReply(QDBusError::Failed, msg);
|
2023-07-24 14:12:59 +08:00
|
|
|
return {};
|
|
|
|
}
|
2023-10-19 14:18:44 +08:00
|
|
|
|
|
|
|
execStr = Actions.value().toString();
|
2023-10-12 18:05:00 +08:00
|
|
|
if (execStr.isEmpty()) {
|
2023-09-01 17:30:14 +08:00
|
|
|
QString msg{"maybe entry actions's format is invalid, abort launch."};
|
|
|
|
qWarning() << msg;
|
|
|
|
sendErrorReply(QDBusError::Failed, msg);
|
2023-07-24 14:12:59 +08:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-07 15:15:08 +08:00
|
|
|
optionsMap.remove("_hooks"); // this is internal property, user shouldn't pass it to Application Manager
|
|
|
|
if (const auto &hooks = parent()->applicationHooks(); !hooks.isEmpty()) {
|
|
|
|
optionsMap.insert("_hooks", hooks);
|
|
|
|
}
|
2023-09-07 15:55:25 +08:00
|
|
|
auto cmds = generateCommand(optionsMap);
|
2023-11-06 11:30:54 +08:00
|
|
|
auto task = unescapeExec(execStr, fields);
|
|
|
|
if (!task) {
|
|
|
|
sendErrorReply(QDBusError::InternalError, "Invalid Command.");
|
|
|
|
return {};
|
|
|
|
}
|
2023-07-24 14:12:59 +08:00
|
|
|
|
2023-11-06 11:30:54 +08:00
|
|
|
auto [bin, execCmds, res] = std::move(task);
|
2023-09-04 15:57:59 +08:00
|
|
|
if (bin.isEmpty()) {
|
|
|
|
qCritical() << "error command is detected, abort.";
|
|
|
|
sendErrorReply(QDBusError::Failed);
|
|
|
|
return {};
|
|
|
|
}
|
2023-07-24 14:12:59 +08:00
|
|
|
|
2023-10-08 17:18:06 +08:00
|
|
|
if (terminal()) {
|
|
|
|
// don't change this sequence
|
2023-10-12 18:05:00 +08:00
|
|
|
execCmds.push_front("-C"); // means run a shellscript
|
|
|
|
execCmds.push_front("--keep-open"); // keep terminal open, prevent exit immediately
|
2023-10-08 17:18:06 +08:00
|
|
|
execCmds.push_front("deepin-terminal");
|
|
|
|
}
|
2023-08-30 18:36:52 +08:00
|
|
|
cmds.append(std::move(execCmds));
|
2023-10-08 17:18:06 +08:00
|
|
|
|
2023-10-07 15:15:08 +08:00
|
|
|
auto &jobManager = parent()->jobManager();
|
2023-07-24 14:12:59 +08:00
|
|
|
return jobManager.addJob(
|
|
|
|
m_applicationPath.path(),
|
2023-09-13 11:47:04 +08:00
|
|
|
[this, binary = std::move(bin), commands = std::move(cmds)](const QVariant &variantValue) mutable -> QVariant {
|
2023-07-24 14:12:59 +08:00
|
|
|
auto resourceFile = variantValue.toString();
|
2023-08-16 17:44:56 +08:00
|
|
|
auto instanceRandomUUID = QUuid::createUuid().toString(QUuid::Id128);
|
|
|
|
auto objectPath = m_applicationPath.path() + "/" + instanceRandomUUID;
|
2023-08-21 16:02:26 +08:00
|
|
|
commands.push_front(QString{"--SourcePath=%1"}.arg(m_desktopSource.sourcePath()));
|
2023-08-20 18:25:59 +08:00
|
|
|
|
2023-09-07 10:15:48 +08:00
|
|
|
auto location = commands.indexOf(R"(%f)");
|
|
|
|
if (location != -1) { // due to std::move, there only remove once
|
|
|
|
commands.remove(location);
|
|
|
|
}
|
|
|
|
|
2023-07-24 14:12:59 +08:00
|
|
|
if (resourceFile.isEmpty()) {
|
2023-08-08 15:10:32 +08:00
|
|
|
commands.push_front(QString{R"(--unitName=app-DDE-%1@%2.service)"}.arg(
|
2023-08-11 10:12:46 +08:00
|
|
|
escapeApplicationId(this->id()), instanceRandomUUID)); // launcher should use this instanceId
|
2023-07-24 14:12:59 +08:00
|
|
|
QProcess process;
|
2023-09-07 10:15:48 +08:00
|
|
|
qDebug() << "run with commands:" << commands;
|
2023-07-24 14:12:59 +08:00
|
|
|
process.start(m_launcher, commands);
|
|
|
|
process.waitForFinished();
|
|
|
|
if (auto code = process.exitCode(); code != 0) {
|
2023-09-05 14:33:21 +08:00
|
|
|
qWarning() << "Launch Application Failed";
|
|
|
|
return QDBusError::Failed;
|
2023-07-24 14:12:59 +08:00
|
|
|
}
|
2023-08-16 17:44:56 +08:00
|
|
|
return objectPath;
|
2023-07-24 14:12:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
auto url = QUrl::fromUserInput(resourceFile);
|
|
|
|
if (!url.isValid()) { // if url is invalid, passing to launcher directly
|
|
|
|
auto scheme = url.scheme();
|
|
|
|
if (!scheme.isEmpty()) {
|
|
|
|
// TODO: resourceFile = processRemoteFile(resourceFile);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-20 18:25:59 +08:00
|
|
|
// NOTE: resourceFile must be available in the following contexts
|
|
|
|
commands.insert(location, resourceFile);
|
|
|
|
commands.push_front(QString{R"(--unitName=DDE-%1@%2.service)"}.arg(this->id(), instanceRandomUUID));
|
2023-07-24 14:12:59 +08:00
|
|
|
QProcess process;
|
2023-09-07 10:15:48 +08:00
|
|
|
qDebug() << "run with commands:" << commands;
|
2023-08-20 18:25:59 +08:00
|
|
|
process.start(getApplicationLauncherBinary(), commands);
|
2023-07-24 14:12:59 +08:00
|
|
|
process.waitForFinished();
|
|
|
|
auto exitCode = process.exitCode();
|
|
|
|
if (exitCode != 0) {
|
2023-09-05 14:33:21 +08:00
|
|
|
qWarning() << "Launch Application Failed";
|
|
|
|
return QDBusError::Failed;
|
2023-07-24 14:12:59 +08:00
|
|
|
}
|
2023-08-16 17:44:56 +08:00
|
|
|
return objectPath;
|
2023-07-24 14:12:59 +08:00
|
|
|
},
|
|
|
|
std::move(res));
|
2023-07-17 14:49:35 +08:00
|
|
|
}
|
|
|
|
|
2023-09-01 15:48:29 +08:00
|
|
|
bool ApplicationService::SendToDesktop() const noexcept
|
|
|
|
{
|
|
|
|
if (isOnDesktop()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto dir = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
|
|
|
|
if (dir.isEmpty()) {
|
|
|
|
qDebug() << "no desktop directory found.";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto desktopFile = QDir{dir}.filePath(m_desktopSource.desktopId() + ".desktop");
|
|
|
|
auto success = m_desktopSource.sourceFileRef().link(desktopFile);
|
|
|
|
if (!success) {
|
|
|
|
qDebug() << "create link failed:" << m_desktopSource.sourceFileRef().errorString() << "path:" << desktopFile;
|
2023-09-01 17:30:14 +08:00
|
|
|
sendErrorReply(QDBusError::ErrorType::Failed, m_desktopSource.sourceFileRef().errorString());
|
2023-09-01 15:48:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ApplicationService::RemoveFromDesktop() const noexcept
|
|
|
|
{
|
|
|
|
if (!isOnDesktop()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto dir = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
|
|
|
|
if (dir.isEmpty()) {
|
|
|
|
qDebug() << "no desktop directory found.";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
QFile desktopFile{QDir{dir}.filePath(m_desktopSource.desktopId() + ".desktop")};
|
|
|
|
auto success = desktopFile.remove();
|
|
|
|
|
|
|
|
if (!success) {
|
|
|
|
qDebug() << "remove desktop file failed:" << desktopFile.errorString();
|
2023-09-01 17:30:14 +08:00
|
|
|
sendErrorReply(QDBusError::ErrorType::Failed, desktopFile.errorString());
|
2023-09-01 15:48:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ApplicationService::isOnDesktop() const noexcept
|
|
|
|
{
|
|
|
|
auto dir = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
|
|
|
|
|
|
|
|
if (dir.isEmpty()) {
|
|
|
|
qDebug() << "no desktop directory found.";
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
QFileInfo info{QDir{dir}.filePath(m_desktopSource.desktopId() + ".desktop")};
|
|
|
|
|
|
|
|
if (!info.exists()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!info.isSymbolicLink()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return info.symLinkTarget() == m_desktopSource.sourcePath();
|
|
|
|
}
|
|
|
|
|
2023-09-07 12:01:08 +08:00
|
|
|
bool ApplicationService::noDisplay() const noexcept
|
|
|
|
{
|
|
|
|
auto val = findEntryValue(DesktopFileEntryKey, "NoDisplay", EntryValueType::Boolean);
|
|
|
|
|
|
|
|
if (val.isNull()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return val.toBool();
|
|
|
|
}
|
|
|
|
|
2023-07-21 14:47:40 +08:00
|
|
|
QStringList ApplicationService::actions() const noexcept
|
|
|
|
{
|
2023-08-30 15:21:20 +08:00
|
|
|
auto val = findEntryValue(DesktopFileEntryKey, "Actions", EntryValueType::String);
|
2023-07-17 14:49:35 +08:00
|
|
|
|
2023-08-30 15:21:20 +08:00
|
|
|
if (val.isNull()) {
|
2023-07-24 14:12:59 +08:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2023-08-30 15:21:20 +08:00
|
|
|
auto actionList = val.toString().split(";", Qt::SkipEmptyParts);
|
|
|
|
return actionList;
|
|
|
|
}
|
|
|
|
|
|
|
|
QStringList ApplicationService::categories() const noexcept
|
|
|
|
{
|
|
|
|
auto val = findEntryValue(DesktopFileEntryKey, "Categories", EntryValueType::String);
|
|
|
|
|
|
|
|
if (val.isNull()) {
|
2023-07-24 14:12:59 +08:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2023-08-30 15:21:20 +08:00
|
|
|
return val.toString().split(';', Qt::SkipEmptyParts);
|
2023-07-21 14:47:40 +08:00
|
|
|
}
|
2023-07-17 14:49:35 +08:00
|
|
|
|
2023-08-25 10:47:17 +08:00
|
|
|
PropMap ApplicationService::actionName() const noexcept
|
|
|
|
{
|
|
|
|
PropMap ret;
|
2023-10-16 14:39:20 +08:00
|
|
|
const auto &actionList = actions();
|
2023-08-25 10:47:17 +08:00
|
|
|
|
2023-10-16 14:39:20 +08:00
|
|
|
for (const auto &action : actionList) {
|
|
|
|
auto rawActionKey = DesktopFileActionKey % action;
|
|
|
|
auto value = m_entry->value(rawActionKey, "Name");
|
2023-08-25 10:47:17 +08:00
|
|
|
if (!value.has_value()) {
|
|
|
|
continue;
|
|
|
|
}
|
2023-10-16 14:39:20 +08:00
|
|
|
ret.insert(action, std::move(value).value().value<QStringMap>());
|
2023-08-25 10:47:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2023-10-16 14:39:20 +08:00
|
|
|
QStringMap ApplicationService::name() const noexcept
|
2023-08-25 10:47:17 +08:00
|
|
|
{
|
2023-09-27 16:19:12 +08:00
|
|
|
auto value = m_entry->value(DesktopFileEntryKey, "Name");
|
|
|
|
if (!value) {
|
2023-10-16 14:39:20 +08:00
|
|
|
return {};
|
2023-09-27 16:19:12 +08:00
|
|
|
}
|
|
|
|
|
2023-10-16 14:39:20 +08:00
|
|
|
if (!value->canConvert<QStringMap>()) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
return value->value<QStringMap>();
|
2023-09-27 16:19:12 +08:00
|
|
|
}
|
|
|
|
|
2023-10-16 14:39:20 +08:00
|
|
|
QStringMap ApplicationService::genericName() const noexcept
|
2023-09-27 16:19:12 +08:00
|
|
|
{
|
|
|
|
auto value = m_entry->value(DesktopFileEntryKey, "GenericName");
|
|
|
|
if (!value) {
|
2023-10-16 14:39:20 +08:00
|
|
|
return {};
|
2023-09-27 16:19:12 +08:00
|
|
|
}
|
|
|
|
|
2023-10-16 14:39:20 +08:00
|
|
|
if (!value->canConvert<QStringMap>()) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
return value->value<QStringMap>();
|
2023-08-25 10:47:17 +08:00
|
|
|
}
|
|
|
|
|
2023-10-16 14:39:20 +08:00
|
|
|
QStringMap ApplicationService::icons() const noexcept
|
2023-08-25 10:47:17 +08:00
|
|
|
{
|
2023-10-16 14:39:20 +08:00
|
|
|
QStringMap ret;
|
2023-08-25 10:47:17 +08:00
|
|
|
auto actionList = actions();
|
|
|
|
for (const auto &action : actionList) {
|
2023-10-25 17:42:02 +08:00
|
|
|
auto actionKey = QString{action}.prepend(DesktopFileActionKey);
|
2023-10-16 14:39:20 +08:00
|
|
|
auto value = m_entry->value(actionKey, "Icon");
|
2023-08-25 10:47:17 +08:00
|
|
|
if (!value.has_value()) {
|
|
|
|
continue;
|
|
|
|
}
|
2023-10-16 14:39:20 +08:00
|
|
|
ret.insert(actionKey, value->value<QString>());
|
2023-08-25 10:47:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
auto mainIcon = m_entry->value(DesktopFileEntryKey, "Icon");
|
|
|
|
if (mainIcon.has_value()) {
|
2023-10-16 14:39:20 +08:00
|
|
|
ret.insert(DesktopFileEntryKey, mainIcon->value<QString>());
|
2023-08-25 10:47:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2023-08-18 10:01:52 +08:00
|
|
|
ObjectMap ApplicationService::GetManagedObjects() const
|
|
|
|
{
|
2023-08-18 17:22:38 +08:00
|
|
|
return dumpDBusObject(m_Instances);
|
2023-08-18 10:01:52 +08:00
|
|
|
}
|
|
|
|
|
2023-07-24 14:12:59 +08:00
|
|
|
QString ApplicationService::id() const noexcept
|
2023-07-21 14:47:40 +08:00
|
|
|
{
|
2023-08-21 16:02:26 +08:00
|
|
|
return m_desktopSource.desktopId();
|
2023-07-21 14:47:40 +08:00
|
|
|
}
|
2023-07-17 14:49:35 +08:00
|
|
|
|
2023-08-31 16:40:29 +08:00
|
|
|
bool ApplicationService::x_Flatpak() const noexcept
|
|
|
|
{
|
|
|
|
auto val = findEntryValue(DesktopFileEntryKey, "X-flatpak", EntryValueType::String);
|
2023-09-01 15:49:30 +08:00
|
|
|
return !val.isNull();
|
2023-08-31 16:40:29 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ApplicationService::x_linglong() const noexcept
|
|
|
|
{
|
|
|
|
auto val = findEntryValue(DesktopFileEntryKey, "X-linglong", EntryValueType::String);
|
2023-09-01 15:49:30 +08:00
|
|
|
return !val.isNull();
|
2023-08-31 16:40:29 +08:00
|
|
|
}
|
|
|
|
|
2023-11-06 15:58:46 +08:00
|
|
|
QString ApplicationService::x_Vendor_Deepin() const noexcept
|
|
|
|
{
|
|
|
|
return findEntryValue(DesktopFileEntryKey, "X-Deepin-Vendor", EntryValueType::String).toString();
|
|
|
|
}
|
|
|
|
|
2023-10-08 17:18:06 +08:00
|
|
|
bool ApplicationService::terminal() const noexcept
|
|
|
|
{
|
|
|
|
auto val = findEntryValue(DesktopFileEntryKey, "Terminal", EntryValueType::String);
|
|
|
|
if (!val.isNull()) {
|
|
|
|
return val.toBool();
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-08-31 16:40:29 +08:00
|
|
|
qulonglong ApplicationService::installedTime() const noexcept
|
|
|
|
{
|
|
|
|
return m_desktopSource.createTime();
|
|
|
|
}
|
|
|
|
|
2023-08-23 17:20:47 +08:00
|
|
|
qulonglong ApplicationService::lastLaunchedTime() const noexcept
|
|
|
|
{
|
|
|
|
return m_lastLaunch;
|
|
|
|
}
|
|
|
|
|
2023-10-08 17:18:06 +08:00
|
|
|
double ApplicationService::scaleFactor() const noexcept
|
|
|
|
{
|
|
|
|
return m_scaleFactor;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ApplicationService::setScaleFactor(double value) noexcept
|
|
|
|
{
|
|
|
|
if (value == m_scaleFactor) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto storagePtr = m_storage.lock();
|
|
|
|
if (!storagePtr) {
|
|
|
|
qCritical() << "broken storage.";
|
|
|
|
sendErrorReply(QDBusError::InternalError);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto appId = id();
|
|
|
|
if (value == 0) {
|
|
|
|
m_customScale = false;
|
|
|
|
m_scaleFactor = getScaleFactor();
|
|
|
|
storagePtr->deleteApplicationValue(appId, ApplicationPropertiesGroup, ScaleFactor);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-10-12 18:05:00 +08:00
|
|
|
if (m_customScale) {
|
2023-10-08 17:18:06 +08:00
|
|
|
if (!storagePtr->updateApplicationValue(appId, ApplicationPropertiesGroup, ScaleFactor, value)) {
|
2023-10-12 18:05:00 +08:00
|
|
|
sendErrorReply(QDBusError::Failed, "update scaleFactor failed.");
|
2023-10-08 17:18:06 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!storagePtr->createApplicationValue(appId, ApplicationPropertiesGroup, ScaleFactor, value)) {
|
|
|
|
sendErrorReply(QDBusError::Failed, "set scaleFactor failed.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_scaleFactor = value;
|
|
|
|
emit scaleFactorChanged();
|
|
|
|
}
|
|
|
|
|
2023-09-15 14:44:27 +08:00
|
|
|
bool ApplicationService::autostartCheck(const QString &linkPath) const noexcept
|
2023-09-06 13:22:01 +08:00
|
|
|
{
|
|
|
|
QFileInfo info{linkPath};
|
|
|
|
|
2023-09-08 10:58:41 +08:00
|
|
|
if (info.exists()) {
|
2023-09-15 14:44:27 +08:00
|
|
|
if (info.isSymbolicLink() and info.symLinkTarget() == m_desktopSource.sourcePath()) {
|
2023-09-08 10:58:41 +08:00
|
|
|
return true;
|
|
|
|
}
|
2023-09-06 13:22:01 +08:00
|
|
|
qWarning() << "same name desktop file exists:" << linkPath << "but this may not created by AM.";
|
|
|
|
}
|
|
|
|
|
2023-09-08 10:58:41 +08:00
|
|
|
return false;
|
2023-09-06 13:22:01 +08:00
|
|
|
}
|
|
|
|
|
2023-07-21 14:47:40 +08:00
|
|
|
bool ApplicationService::isAutoStart() const noexcept
|
|
|
|
{
|
2023-09-06 13:22:01 +08:00
|
|
|
auto path = getAutoStartDirs().first();
|
|
|
|
auto linkName = QDir{path}.filePath(m_desktopSource.desktopId() + ".desktop");
|
|
|
|
return autostartCheck(linkName);
|
2023-07-21 14:47:40 +08:00
|
|
|
}
|
2023-07-17 14:49:35 +08:00
|
|
|
|
2023-07-21 14:47:40 +08:00
|
|
|
void ApplicationService::setAutoStart(bool autostart) noexcept
|
|
|
|
{
|
2023-09-04 18:18:20 +08:00
|
|
|
auto path = getAutoStartDirs().first();
|
|
|
|
auto linkName = QDir{path}.filePath(m_desktopSource.desktopId() + ".desktop");
|
|
|
|
auto &file = m_desktopSource.sourceFileRef();
|
|
|
|
|
|
|
|
if (autostart) {
|
2023-09-06 13:22:01 +08:00
|
|
|
if (!autostartCheck(linkName) and !file.link(linkName)) {
|
2023-09-04 18:18:20 +08:00
|
|
|
qWarning() << "link to autostart failed:" << file.errorString();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
2023-09-06 13:22:01 +08:00
|
|
|
if (autostartCheck(linkName)) {
|
|
|
|
QFile linkFile{linkName};
|
|
|
|
if (!linkFile.remove()) {
|
|
|
|
qWarning() << "remove link failed:" << linkFile.errorString();
|
|
|
|
return;
|
|
|
|
}
|
2023-09-04 18:18:20 +08:00
|
|
|
}
|
|
|
|
}
|
2023-09-08 10:58:41 +08:00
|
|
|
|
|
|
|
emit autostartChanged();
|
2023-07-21 14:47:40 +08:00
|
|
|
}
|
2023-07-17 14:49:35 +08:00
|
|
|
|
2023-09-20 18:29:42 +08:00
|
|
|
QStringList ApplicationService::mimeTypes() const noexcept
|
|
|
|
{
|
|
|
|
QStringList ret;
|
|
|
|
const auto &desktopFilePath = m_desktopSource.sourcePath();
|
|
|
|
const auto &cacheList = parent()->mimeManager().infos();
|
|
|
|
auto cache = std::find_if(cacheList.cbegin(), cacheList.cend(), [&desktopFilePath](const MimeInfo &info) {
|
|
|
|
return desktopFilePath.startsWith(info.directory());
|
|
|
|
});
|
|
|
|
|
|
|
|
const auto &info = cache->cacheInfo();
|
|
|
|
if (info) {
|
|
|
|
ret.append(info->queryTypes(id()));
|
|
|
|
}
|
|
|
|
|
|
|
|
AppList tmp;
|
|
|
|
|
|
|
|
for (auto it = cacheList.crbegin(); it != cacheList.crend(); ++it) {
|
|
|
|
const auto &list = it->appsList();
|
|
|
|
std::for_each(list.crbegin(), list.crend(), [&tmp, this](const MimeApps &app) {
|
|
|
|
auto [added, removed] = app.queryTypes(id());
|
|
|
|
tmp.added.append(std::move(added));
|
|
|
|
tmp.removed.append(std::move(removed));
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
tmp.added.removeDuplicates();
|
|
|
|
tmp.removed.removeDuplicates();
|
|
|
|
for (const auto &it : tmp.removed) {
|
|
|
|
tmp.added.removeOne(it);
|
|
|
|
}
|
|
|
|
|
|
|
|
ret.append(std::move(tmp.added));
|
|
|
|
ret.removeDuplicates();
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ApplicationService::setMimeTypes(const QStringList &value) noexcept
|
|
|
|
{
|
|
|
|
auto oldMimes = mimeTypes();
|
|
|
|
auto newMimes = value;
|
|
|
|
std::sort(oldMimes.begin(), oldMimes.end());
|
|
|
|
std::sort(newMimes.begin(), newMimes.end());
|
|
|
|
|
|
|
|
QStringList newAdds;
|
|
|
|
QStringList newRemoved;
|
|
|
|
|
|
|
|
std::set_difference(oldMimes.begin(), oldMimes.end(), newMimes.begin(), newMimes.end(), std::back_inserter(newRemoved));
|
|
|
|
std::set_difference(newMimes.begin(), newMimes.end(), oldMimes.begin(), oldMimes.end(), std::back_inserter(newAdds));
|
|
|
|
|
|
|
|
static QString userDir = getXDGConfigHome();
|
|
|
|
auto &infos = parent()->mimeManager().infos();
|
|
|
|
auto userInfo = std::find_if(infos.begin(), infos.end(), [](const MimeInfo &info) { return info.directory() == userDir; });
|
|
|
|
if (userInfo == infos.cend()) {
|
|
|
|
sendErrorReply(QDBusError::Failed, "user-specific config file doesn't exists.");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto &list = userInfo->appsList().rbegin();
|
|
|
|
const auto &appId = id();
|
|
|
|
for (const auto &add : newAdds) {
|
|
|
|
list->addAssociation(add, appId);
|
|
|
|
}
|
|
|
|
for (const auto &remove : newRemoved) {
|
|
|
|
list->removeAssociation(remove, appId);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!list->writeToFile()) {
|
|
|
|
qWarning() << "error occurred when write mime association to file";
|
|
|
|
}
|
|
|
|
|
|
|
|
emit MimeTypesChanged();
|
|
|
|
}
|
|
|
|
|
2023-07-21 14:47:40 +08:00
|
|
|
QList<QDBusObjectPath> ApplicationService::instances() const noexcept
|
|
|
|
{
|
|
|
|
return m_Instances.keys();
|
|
|
|
}
|
2023-07-17 14:49:35 +08:00
|
|
|
|
2023-09-13 16:52:53 +08:00
|
|
|
bool ApplicationService::addOneInstance(const QString &instanceId,
|
|
|
|
const QString &application,
|
|
|
|
const QString &systemdUnitPath,
|
|
|
|
const QString &launcher)
|
2023-07-21 14:47:40 +08:00
|
|
|
{
|
2023-09-13 16:52:53 +08:00
|
|
|
auto *service = new InstanceService{instanceId, application, systemdUnitPath, launcher};
|
2023-08-20 18:25:59 +08:00
|
|
|
auto *adaptor = new InstanceAdaptor(service);
|
2023-08-16 17:44:56 +08:00
|
|
|
QString objectPath{m_applicationPath.path() + "/" + instanceId};
|
2023-07-24 14:12:59 +08:00
|
|
|
|
2023-08-28 11:11:50 +08:00
|
|
|
if (registerObjectToDBus(service, objectPath, InstanceInterface)) {
|
2023-07-24 14:12:59 +08:00
|
|
|
m_Instances.insert(QDBusObjectPath{objectPath}, QSharedPointer<InstanceService>{service});
|
|
|
|
service->moveToThread(this->thread());
|
|
|
|
adaptor->moveToThread(this->thread());
|
2023-08-25 10:47:17 +08:00
|
|
|
emit InterfacesAdded(QDBusObjectPath{objectPath}, getChildInterfacesAndPropertiesFromObject(service));
|
2023-07-24 14:12:59 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
adaptor->deleteLater();
|
|
|
|
service->deleteLater();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-08-20 18:25:59 +08:00
|
|
|
void ApplicationService::removeOneInstance(const QDBusObjectPath &instance) noexcept
|
2023-07-24 14:12:59 +08:00
|
|
|
{
|
2023-08-18 10:01:52 +08:00
|
|
|
if (auto it = m_Instances.find(instance); it != m_Instances.cend()) {
|
2023-08-25 10:47:17 +08:00
|
|
|
emit InterfacesRemoved(instance, getChildInterfacesFromObject(it->data()));
|
2023-08-18 10:01:52 +08:00
|
|
|
unregisterObjectFromDBus(instance.path());
|
|
|
|
m_Instances.remove(instance);
|
|
|
|
}
|
2023-07-21 14:47:40 +08:00
|
|
|
}
|
2023-07-17 14:49:35 +08:00
|
|
|
|
2023-08-20 18:25:59 +08:00
|
|
|
void ApplicationService::removeAllInstance() noexcept
|
2023-07-21 14:47:40 +08:00
|
|
|
{
|
2023-07-24 14:12:59 +08:00
|
|
|
for (const auto &instance : m_Instances.keys()) {
|
|
|
|
removeOneInstance(instance);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-14 17:16:10 +08:00
|
|
|
void ApplicationService::detachAllInstance() noexcept
|
|
|
|
{
|
|
|
|
for (auto &instance : m_Instances.values()) {
|
|
|
|
orphanedInstances->append(instance);
|
2023-09-14 17:57:44 +08:00
|
|
|
instance->setProperty("Orphaned", true);
|
2023-09-14 17:16:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
m_Instances.clear();
|
|
|
|
}
|
|
|
|
|
2023-07-24 14:12:59 +08:00
|
|
|
QDBusObjectPath ApplicationService::findInstance(const QString &instanceId) const
|
|
|
|
{
|
2023-09-06 16:49:47 +08:00
|
|
|
for (auto it = m_Instances.constKeyValueBegin(); it != m_Instances.constKeyValueEnd(); ++it) {
|
|
|
|
const auto &[path, ptr] = *it;
|
2023-07-24 14:12:59 +08:00
|
|
|
if (ptr->instanceId() == instanceId) {
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2023-08-20 18:25:59 +08:00
|
|
|
void ApplicationService::resetEntry(DesktopEntry *newEntry) noexcept
|
|
|
|
{
|
|
|
|
m_entry.reset(newEntry);
|
2023-09-15 13:49:34 +08:00
|
|
|
emit autostartChanged();
|
|
|
|
emit noDisplayChanged();
|
|
|
|
emit isOnDesktopChanged();
|
|
|
|
emit installedTimeChanged();
|
|
|
|
emit x_FlatpakChanged();
|
|
|
|
emit x_linglongChanged();
|
|
|
|
emit instanceChanged();
|
|
|
|
emit lastLaunchedTimeChanged();
|
|
|
|
emit iconsChanged();
|
2023-09-27 16:19:12 +08:00
|
|
|
emit nameChanged();
|
|
|
|
emit genericNameChanged();
|
2023-09-15 13:49:34 +08:00
|
|
|
emit actionNameChanged();
|
|
|
|
emit actionsChanged();
|
|
|
|
emit categoriesChanged();
|
2023-09-20 18:29:42 +08:00
|
|
|
emit MimeTypesChanged();
|
2023-10-08 17:18:06 +08:00
|
|
|
emit terminalChanged();
|
|
|
|
emit scaleFactorChanged();
|
2023-08-20 18:25:59 +08:00
|
|
|
}
|
|
|
|
|
2023-11-06 11:30:54 +08:00
|
|
|
std::optional<QStringList> ApplicationService::unescapeExecArgs(const QString &str) noexcept
|
2023-07-24 14:12:59 +08:00
|
|
|
{
|
2023-10-19 14:18:44 +08:00
|
|
|
auto unescapedStr = unescape(str, true);
|
|
|
|
if (unescapedStr.isEmpty()) {
|
|
|
|
qWarning() << "unescape Exec failed.";
|
2023-11-06 11:30:54 +08:00
|
|
|
return std::nullopt;
|
2023-10-19 14:18:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
auto deleter = [](wordexp_t *word) {
|
|
|
|
wordfree(word);
|
|
|
|
delete word;
|
|
|
|
};
|
2023-09-04 15:57:59 +08:00
|
|
|
std::unique_ptr<wordexp_t, decltype(deleter)> words{new (std::nothrow) wordexp_t{0, nullptr, 0}, deleter};
|
|
|
|
|
2023-10-19 14:18:44 +08:00
|
|
|
if (auto ret = wordexp(unescapedStr.toLocal8Bit(), words.get(), WRDE_SHOWERR); ret != 0) {
|
2023-09-04 15:57:59 +08:00
|
|
|
if (ret != 0) {
|
|
|
|
QString errMessage;
|
|
|
|
switch (ret) {
|
|
|
|
case WRDE_BADCHAR:
|
|
|
|
errMessage = "BADCHAR";
|
2023-11-06 11:30:54 +08:00
|
|
|
break;
|
2023-09-04 15:57:59 +08:00
|
|
|
case WRDE_BADVAL:
|
|
|
|
errMessage = "BADVAL";
|
|
|
|
break;
|
|
|
|
case WRDE_CMDSUB:
|
|
|
|
errMessage = "CMDSUB";
|
|
|
|
break;
|
|
|
|
case WRDE_NOSPACE:
|
|
|
|
errMessage = "NOSPACE";
|
|
|
|
break;
|
|
|
|
case WRDE_SYNTAX:
|
|
|
|
errMessage = "SYNTAX";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
errMessage = "unknown";
|
|
|
|
}
|
|
|
|
qWarning() << "wordexp error: " << errMessage;
|
2023-11-06 11:30:54 +08:00
|
|
|
return std::nullopt;
|
2023-09-04 15:57:59 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QStringList execList;
|
|
|
|
for (int i = 0; i < words->we_wordc; ++i) {
|
|
|
|
execList.emplace_back(words->we_wordv[i]);
|
|
|
|
}
|
2023-10-19 14:18:44 +08:00
|
|
|
|
|
|
|
return execList;
|
|
|
|
}
|
|
|
|
|
|
|
|
LaunchTask ApplicationService::unescapeExec(const QString &str, const QStringList &fields) noexcept
|
|
|
|
{
|
|
|
|
LaunchTask task;
|
2023-11-06 11:30:54 +08:00
|
|
|
auto opt = unescapeExecArgs(str);
|
2023-07-24 14:12:59 +08:00
|
|
|
|
2023-11-06 11:30:54 +08:00
|
|
|
if (!opt.has_value()) {
|
|
|
|
qWarning() << "unescapeExecArgs failed.";
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
auto execList = std::move(opt).value();
|
|
|
|
if (execList.isEmpty()) {
|
|
|
|
qWarning() << "exec format is invalid.";
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
task.LaunchBin = execList.first();
|
2023-07-24 14:12:59 +08:00
|
|
|
QRegularExpression re{"%[fFuUickdDnNvm]"};
|
|
|
|
auto matcher = re.match(str);
|
|
|
|
if (!matcher.hasMatch()) {
|
|
|
|
task.command.append(std::move(execList));
|
2023-08-25 18:21:11 +08:00
|
|
|
task.Resources.emplace_back(QString{""}); // mapReduce should run once at least
|
2023-07-24 14:12:59 +08:00
|
|
|
return task;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto list = matcher.capturedTexts();
|
|
|
|
if (list.count() > 1) {
|
|
|
|
qWarning() << "invalid exec format, all filed code will be ignored.";
|
|
|
|
for (const auto &code : list) {
|
|
|
|
execList.removeOne(code);
|
|
|
|
}
|
|
|
|
task.command.append(std::move(execList));
|
|
|
|
return task;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto filesCode = list.first().back().toLatin1();
|
|
|
|
auto codeStr = QString(R"(%%1)").arg(filesCode);
|
|
|
|
auto location = execList.indexOf(codeStr);
|
|
|
|
|
|
|
|
switch (filesCode) {
|
2023-08-25 16:04:55 +08:00
|
|
|
case 'f': { // Defer to async job
|
|
|
|
task.command.append(std::move(execList));
|
2023-09-04 15:57:59 +08:00
|
|
|
for (const auto &field : fields) {
|
|
|
|
task.Resources.emplace_back(field);
|
2023-07-24 14:12:59 +08:00
|
|
|
}
|
2023-09-04 15:57:59 +08:00
|
|
|
} break;
|
2023-08-25 16:04:55 +08:00
|
|
|
case 'u': {
|
|
|
|
execList.removeAt(location);
|
|
|
|
if (fields.empty()) {
|
2023-07-24 14:12:59 +08:00
|
|
|
task.command.append(execList);
|
|
|
|
break;
|
|
|
|
}
|
2023-08-25 16:04:55 +08:00
|
|
|
if (fields.count() > 1) {
|
|
|
|
qDebug() << R"(fields count is greater than one, %u will only take first element.)";
|
2023-07-24 14:12:59 +08:00
|
|
|
}
|
2023-08-25 16:04:55 +08:00
|
|
|
execList.insert(location, fields.first());
|
|
|
|
task.command.append(execList);
|
2023-09-04 15:57:59 +08:00
|
|
|
} break;
|
2023-08-25 16:04:55 +08:00
|
|
|
case 'F':
|
|
|
|
[[fallthrough]];
|
|
|
|
case 'U': {
|
|
|
|
execList.removeAt(location);
|
|
|
|
auto it = execList.begin() + location;
|
|
|
|
for (const auto &field : fields) {
|
|
|
|
it = execList.insert(it, field);
|
|
|
|
}
|
|
|
|
task.command.append(std::move(execList));
|
2023-09-04 15:57:59 +08:00
|
|
|
} break;
|
2023-08-25 16:04:55 +08:00
|
|
|
case 'i': {
|
|
|
|
execList.removeAt(location);
|
|
|
|
auto val = m_entry->value(DesktopFileEntryKey, "Icon");
|
|
|
|
if (!val) {
|
|
|
|
qDebug() << R"(Application Icons can't be found. %i will be ignored.)";
|
2023-07-24 14:12:59 +08:00
|
|
|
task.command.append(std::move(execList));
|
2023-08-25 16:04:55 +08:00
|
|
|
return task;
|
2023-07-24 14:12:59 +08:00
|
|
|
}
|
2023-10-12 18:05:00 +08:00
|
|
|
|
|
|
|
auto iconStr = toIconString(val.value());
|
|
|
|
if (iconStr.isEmpty()) {
|
2023-08-25 16:04:55 +08:00
|
|
|
qDebug() << R"(Icons Convert to string failed. %i will be ignored.)";
|
2023-07-24 14:12:59 +08:00
|
|
|
task.command.append(std::move(execList));
|
2023-08-25 16:04:55 +08:00
|
|
|
return task;
|
2023-07-24 14:12:59 +08:00
|
|
|
}
|
2023-08-25 16:04:55 +08:00
|
|
|
auto it = execList.insert(location, iconStr);
|
|
|
|
execList.insert(it, "--icon");
|
|
|
|
task.command.append(std::move(execList));
|
2023-09-04 15:57:59 +08:00
|
|
|
} break;
|
2023-08-25 16:04:55 +08:00
|
|
|
case 'c': {
|
|
|
|
execList.removeAt(location);
|
|
|
|
auto val = m_entry->value(DesktopFileEntryKey, "Name");
|
|
|
|
if (!val) {
|
|
|
|
qDebug() << R"(Application Name can't be found. %c will be ignored.)";
|
2023-07-24 14:12:59 +08:00
|
|
|
task.command.append(std::move(execList));
|
2023-08-25 16:04:55 +08:00
|
|
|
return task;
|
2023-07-24 14:12:59 +08:00
|
|
|
}
|
2023-10-12 18:05:00 +08:00
|
|
|
|
|
|
|
const auto &rawValue = val.value();
|
|
|
|
if (!rawValue.canConvert<QStringMap>()) {
|
|
|
|
qDebug() << "Name's underlying type mismatch:"
|
|
|
|
<< "QStringMap" << rawValue.metaType().name();
|
|
|
|
task.command.append(std::move(execList));
|
|
|
|
return task;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto NameStr = toLocaleString(rawValue.value<QStringMap>(), getUserLocale());
|
|
|
|
if (NameStr.isEmpty()) {
|
2023-08-25 16:04:55 +08:00
|
|
|
qDebug() << R"(Name Convert to locale string failed. %c will be ignored.)";
|
2023-07-24 14:12:59 +08:00
|
|
|
task.command.append(std::move(execList));
|
2023-08-25 16:04:55 +08:00
|
|
|
return task;
|
2023-07-24 14:12:59 +08:00
|
|
|
}
|
2023-08-25 16:04:55 +08:00
|
|
|
execList.insert(location, NameStr);
|
|
|
|
task.command.append(std::move(execList));
|
2023-09-04 15:57:59 +08:00
|
|
|
} break;
|
2023-08-25 16:04:55 +08:00
|
|
|
case 'k': { // ignore all desktop file location for now.
|
|
|
|
execList.removeAt(location);
|
|
|
|
task.command.append(std::move(execList));
|
2023-09-04 15:57:59 +08:00
|
|
|
} break;
|
2023-08-25 16:04:55 +08:00
|
|
|
case 'd':
|
|
|
|
case 'D':
|
|
|
|
case 'n':
|
|
|
|
case 'N':
|
|
|
|
case 'v':
|
|
|
|
[[fallthrough]]; // Deprecated field codes should be removed from the command line and ignored.
|
|
|
|
case 'm': {
|
|
|
|
execList.removeAt(location);
|
|
|
|
task.command.append(std::move(execList));
|
2023-09-04 15:57:59 +08:00
|
|
|
} break;
|
|
|
|
default: {
|
|
|
|
qDebug() << "unrecognized file code.";
|
2023-08-25 16:04:55 +08:00
|
|
|
}
|
2023-07-24 14:12:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (task.Resources.isEmpty()) {
|
|
|
|
task.Resources.emplace_back(QString{""}); // mapReduce should run once at least
|
|
|
|
}
|
|
|
|
|
|
|
|
return task;
|
2023-07-21 14:47:40 +08:00
|
|
|
}
|
2023-08-30 15:21:20 +08:00
|
|
|
|
|
|
|
QVariant ApplicationService::findEntryValue(const QString &group,
|
|
|
|
const QString &valueKey,
|
|
|
|
EntryValueType type,
|
|
|
|
const QLocale &locale) const noexcept
|
|
|
|
{
|
|
|
|
QVariant ret;
|
|
|
|
auto tmp = m_entry->value(group, valueKey);
|
|
|
|
if (!tmp.has_value()) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto val = std::move(tmp).value();
|
|
|
|
bool ok{false};
|
|
|
|
|
|
|
|
switch (type) {
|
2023-10-18 18:29:52 +08:00
|
|
|
case EntryValueType::Raw: {
|
|
|
|
auto valStr = val.toString();
|
|
|
|
if (!valStr.isEmpty()) {
|
|
|
|
ret = QVariant::fromValue(valStr);
|
|
|
|
}
|
|
|
|
} break;
|
2023-08-30 15:21:20 +08:00
|
|
|
case EntryValueType::String: {
|
2023-10-12 18:05:00 +08:00
|
|
|
auto valStr = toString(val);
|
|
|
|
if (!valStr.isEmpty()) {
|
2023-08-30 15:21:20 +08:00
|
|
|
ret = QVariant::fromValue(valStr);
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case EntryValueType::LocaleString: {
|
2023-10-12 18:05:00 +08:00
|
|
|
if (!val.canConvert<QStringMap>()) {
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
auto valStr = toLocaleString(val.value<QStringMap>(), locale);
|
|
|
|
if (!valStr.isEmpty()) {
|
2023-08-30 15:21:20 +08:00
|
|
|
ret = QVariant::fromValue(valStr);
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case EntryValueType::Boolean: {
|
2023-10-12 18:05:00 +08:00
|
|
|
auto valBool = toBoolean(val, ok);
|
2023-08-30 15:21:20 +08:00
|
|
|
if (ok) {
|
|
|
|
ret = QVariant::fromValue(valBool);
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
case EntryValueType::IconString: {
|
2023-10-12 18:05:00 +08:00
|
|
|
auto valStr = toIconString(val);
|
|
|
|
if (!valStr.isEmpty()) {
|
2023-08-30 15:21:20 +08:00
|
|
|
ret = QVariant::fromValue(valStr);
|
|
|
|
}
|
|
|
|
} break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2023-09-07 15:55:25 +08:00
|
|
|
|
2023-09-14 17:16:10 +08:00
|
|
|
void ApplicationService::updateAfterLaunch(bool isLaunch) noexcept
|
|
|
|
{
|
|
|
|
if (!isLaunch) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto timestamp = QDateTime::currentMSecsSinceEpoch();
|
|
|
|
|
|
|
|
if (auto ptr = m_storage.lock(); ptr) {
|
|
|
|
ptr->updateApplicationValue(
|
|
|
|
m_desktopSource.desktopId(), ApplicationPropertiesGroup, ::LastLaunchedTime, QVariant::fromValue(timestamp));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-08 17:18:06 +08:00
|
|
|
double getScaleFactor() noexcept
|
|
|
|
{
|
|
|
|
auto sessionBus = QDBusConnection::sessionBus();
|
|
|
|
QDBusMessage reply1 = sessionBus.call(QDBusMessage::createMethodCall(
|
|
|
|
"org.deepin.dde.XSettings1", "/org/deepin/dde/XSettings1", "org.deepin.dde.XSettings1", "GetScaleFactor"));
|
|
|
|
|
|
|
|
if (reply1.type() != QDBusMessage::ReplyMessage) {
|
|
|
|
qWarning() << "call GetScaleFactor Failed:" << reply1.errorMessage();
|
|
|
|
return 1.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
QDBusReply<double> ret1(reply1);
|
|
|
|
double scale = ret1.isValid() ? ret1.value() : 1.0;
|
|
|
|
scale = scale > 0 ? scale : 1;
|
|
|
|
return scale;
|
|
|
|
}
|
|
|
|
|
2023-09-07 15:55:25 +08:00
|
|
|
QString getDeepinWineScaleFactor(const QString &appId) noexcept
|
|
|
|
{
|
|
|
|
qCritical() << "Don't using env to control the window scale factor, this function"
|
|
|
|
"should via using graphisc server(Wayland Compositor/Xorg Xft) in deepin wine.";
|
|
|
|
|
|
|
|
QString factor{"1.0"};
|
|
|
|
auto objectPath = QString{"/dde_launcher/org_deepin_dde_launcher/%1"}.arg(getCurrentUID());
|
|
|
|
auto systemBus = QDBusConnection::systemBus();
|
|
|
|
|
|
|
|
auto msg = QDBusMessage::createMethodCall(
|
|
|
|
"org.desktopspec.ConfigManager", objectPath, "org.desktopspec.ConfigManager.Manager", "value");
|
|
|
|
msg.setArguments({QString{"Apps_Disable_Scaling"}});
|
|
|
|
auto reply = systemBus.call(msg);
|
|
|
|
|
|
|
|
if (reply.type() != QDBusMessage::ReplyMessage) {
|
|
|
|
qWarning() << "get Apps_Disable_Scaling failed:" << reply.errorMessage();
|
|
|
|
return factor;
|
|
|
|
}
|
|
|
|
|
|
|
|
QDBusReply<QDBusVariant> ret{reply};
|
|
|
|
if (!ret.isValid()) {
|
|
|
|
qWarning() << "invalid reply:" << ret.error();
|
|
|
|
return factor;
|
|
|
|
}
|
|
|
|
|
|
|
|
QVariantList appList;
|
|
|
|
ret.value().variant().value<QDBusArgument>() >> appList;
|
|
|
|
|
|
|
|
for (const auto &val : appList) {
|
|
|
|
if (val.value<QString>() == appId) {
|
|
|
|
return factor;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-08 17:18:06 +08:00
|
|
|
auto scale = getScaleFactor();
|
2023-09-07 15:55:25 +08:00
|
|
|
factor = QString::number(scale, 'f', -1);
|
|
|
|
return factor;
|
|
|
|
}
|