Compare commits

...

10 Commits

Author SHA1 Message Date
ComixHe
0695c9784c refactor: reimplement unescapeExec
Some checks failed
Call build-distribution / check_job (push) Waiting to run
backup to gitlab / backup-to-gitlabwh (push) Has been cancelled
backup to gitlab / backup-to-gitee (push) Has been cancelled
adjusted patch from master branch

Signed-off-by: ComixHe <heyuming@deepin.org>
2025-04-09 18:17:11 +08:00
Tsic Liu
538cfb48d4 chore: bump version 1.2.16
bump version 1.2.16

log: as title
2024-10-10 07:37:51 +00:00
Tsic Liu
230636f2e1 feat: add startupwmclass prop export
log: as title
2024-09-10 09:55:40 +08:00
Tsic Liu
5b99e9e4b9 feat: add cmake for find am dbus api
log: as title
2024-09-05 13:41:27 +08:00
Yutao Meng
18a176c223 fix: Unable to run program with options
Issue: https://pms.uniontech.com/bug-view-243475.html
Log: Unable to run program with options
Signed-off-by: Yutao Meng <mengyutao@deepin.org>
2024-08-28 14:22:42 +08:00
ComixHe
0e6c21360c fix: change canConvert(int) to canConvert<typename T>()
method canConvert(int) has been deprecated

Signed-off-by: ComixHe <heyuming@deepin.org>
2024-08-13 09:41:52 +08:00
ComixHe
57fbf5f681 fix: uninstantiated static_assert(false, "") was ill-formed
This is c++ language core issue, refactoring the implementation of dumpDBusObject.

core issue:
https://cplusplus.github.io/CWG/issues/2518.html

gcc has fixed this bug since version 13.1:
relative-commit: https://gcc.gnu.org/git/gitweb.cgi?p=gcc.git;h=9944ca17c0766623bce260684edc614def7ea761

Signed-off-by: ComixHe <heyuming@deepin.org>
2024-08-13 09:41:52 +08:00
ck
7939cd568a chore: bump version to 1.2.15
release 1.2.15

Log: bump version to 1.2.15
2024-07-11 10:44:16 +08:00
ck
88b0159cc3 fix: deepin-home checkauto start failed
初次启动时对比filepath 还需要看看 X-Deepin-GenerateSource

Issue: https://github.com/linuxdeepin/developer-center/issues/9638
2024-07-10 17:04:06 +08:00
ck
50a0ad53ec feat: add app extra/unset env config
to fix #8667 you can
- subpath:"/FoxitReader" appExtraEnvironments : "QT_QPA_PLATFORM=xcb"
- subpath:"/FoxitReader" appEnvironmentsBlacklist : "QT_QPA_PLATFORM"

```
dde-dconfig set -a org.deepin.dde.application-manager -r org.deepin.dde.application-manager -k appExtraEnvironments -s "/FoxitReader" -v "[\"QT_QPA_PLATFORM=xcb\"]"
dde-dconfig set -a org.deepin.dde.application-manager -r org.deepin.dde.application-manager -k appEnvironmentsBlacklist -s "/FoxitReader" -v "[\"QT_QPA_PLATFORM\"]"
```

Issue: https://github.com/linuxdeepin/developer-center/issues/8667
2024-06-13 11:32:10 +08:00
19 changed files with 467 additions and 215 deletions

9
.gitignore vendored
View File

@ -1,10 +1,13 @@
.cache
build* build*
.vscode .vscode
/debian/.debhelper /debian/.debhelper
/debian/dde-application-manager /debian/dde-application-manager
/debian/dde-application-manager.substvars /debian/dde-application-manager-api
/debian/dde-application-manager.debhelper.log /debian/*.substvars
/debian/*.debhelper
/debian/debhelper-build-stamp /debian/debhelper-build-stamp
/debian/files /debian/files
/obj-x86_64-linux-gnu /debian/tmp
/obj-*-linux-gnu
*.user *.user

View File

@ -23,6 +23,11 @@ Files: .gitignore
Copyright: None Copyright: None
License: CC0-1.0 License: CC0-1.0
# cmake
Files: api/*.cmake.in
Copyright: None
License: CC0-1.0
# DBus API # DBus API
Files: api/dbus/*.xml apps/app-update-notifier/api/dbus/*.xml Files: api/dbus/*.xml apps/app-update-notifier/api/dbus/*.xml
Copyright: None Copyright: None

View File

@ -2,5 +2,13 @@ include(GNUInstallDirs)
file(GLOB DBusAPI ${CMAKE_CURRENT_LIST_DIR}/dbus/*.xml) file(GLOB DBusAPI ${CMAKE_CURRENT_LIST_DIR}/dbus/*.xml)
configure_file(
${CMAKE_SOURCE_DIR}/api/DDEApplicationManagerConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/DDEApplicationManagerConfig.cmake
@ONLY)
install(FILES ${DBusAPI} install(FILES ${DBusAPI}
DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/dde-application-manager/) DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/dde-application-manager/)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/DDEApplicationManagerConfig.cmake
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/DDEApplicationManager/)

View File

@ -0,0 +1 @@
set(DDE_APPLICATION_MANAGER_DBUS_API_DIR @CMAKE_INSTALL_FULL_DATAROOTDIR@/dde-application-manager/)

View File

@ -117,6 +117,14 @@
<annotation name="org.qtproject.QtDBus.QtTypeName" value="QStringMap"/> <annotation name="org.qtproject.QtDBus.QtTypeName" value="QStringMap"/>
</property> </property>
<property name="StartupWMClass" type="s" access="read">
<annotation
name="org.freedesktop.DBus.Description"
value="The meaning of this property's type is same as which in StartupWMClass."
/>
<annotation name="org.qtproject.QtDBus.QtTypeName" value="QStringMap"/>
</property>
<method name="Launch"> <method name="Launch">
<arg type="s" name="action" direction="in" /> <arg type="s" name="action" direction="in" />
<arg type="as" name="fields" direction="in" /> <arg type="as" name="fields" direction="in" />
@ -138,10 +146,13 @@
1. `uid` (type u): 1. `uid` (type u):
The user id as who is that application will be run. The user id as who is that application will be run.
This option might request a polikit authentication. This option might request a polikit authentication.
2. `env` (type s): 2. `env` (type as):
passing some specific environment variables to Launch passing some specific environment variables to Launch
this application, eg. 'LANG=en_US;PATH=xxx:yyy;' this application without them, eg. '[LANG=en_US, PATH=xxx:yyy]'
3. `path` (type s): 3. `unsetEnv` (type as):
passed environment variables will be ignored when
launching this application, eg. '[LANG, PATH]'
4. `path` (type s):
set this application's working directory, please pass set this application's working directory, please pass
absolute directory path. absolute directory path.
NOTE: NOTE:

View File

@ -11,6 +11,7 @@
#include <algorithm> #include <algorithm>
#include <cstdlib> #include <cstdlib>
#include <map> #include <map>
#include <list>
#include <thread> #include <thread>
#include "constant.h" #include "constant.h"
#include "types.h" #include "types.h"
@ -140,6 +141,7 @@ int processExecStart(msg_ptr &msg, const std::deque<std::string_view> &execArgs)
DBusValueType getPropType(std::string_view key) DBusValueType getPropType(std::string_view key)
{ {
static std::unordered_map<std::string_view, DBusValueType> map{{"Environment", DBusValueType::ArrayOfString}, static std::unordered_map<std::string_view, DBusValueType> map{{"Environment", DBusValueType::ArrayOfString},
{"UnsetEnvironment", DBusValueType::ArrayOfString},
{"WorkingDirectory", DBusValueType::String}, {"WorkingDirectory", DBusValueType::String},
{"ExecSearchPath", DBusValueType::ArrayOfString}}; {"ExecSearchPath", DBusValueType::ArrayOfString}};
@ -150,7 +152,7 @@ DBusValueType getPropType(std::string_view key)
return DBusValueType::String; // fallback to string return DBusValueType::String; // fallback to string
} }
int appendPropValue(msg_ptr &msg, DBusValueType type, std::string_view value) int appendPropValue(msg_ptr &msg, DBusValueType type, const std::list<std::string_view> &value)
{ {
int ret; int ret;
@ -165,9 +167,11 @@ int appendPropValue(msg_ptr &msg, DBusValueType type, std::string_view value)
return ret; return ret;
} }
if (ret = handler->appendValue(std::string{value}); ret < 0) { for (const auto &v : value) {
sd_journal_perror("append property's variant value failed."); if (ret = handler->appendValue(std::string{v}); ret < 0) {
return ret; sd_journal_perror("append property's variant value failed.");
return ret;
}
} }
if (ret = handler->closeVariant(); ret < 0) { if (ret = handler->closeVariant(); ret < 0) {
@ -178,13 +182,12 @@ int appendPropValue(msg_ptr &msg, DBusValueType type, std::string_view value)
return 0; return 0;
} }
int processKVPair(msg_ptr &msg, const std::map<std::string_view, std::string_view> &props) int processKVPair(msg_ptr &msg, const std::map<std::string_view, std::list<std::string_view>> &props)
{ {
int ret; int ret;
if (!props.empty()) { if (!props.empty()) {
for (auto [key, value] : props) { for (auto [key, value] : props) {
std::string keyStr{key}; std::string keyStr{key};
std::string valueStr{value};
if (ret = sd_bus_message_open_container(msg, SD_BUS_TYPE_STRUCT, "sv"); ret < 0) { if (ret = sd_bus_message_open_container(msg, SD_BUS_TYPE_STRUCT, "sv"); ret < 0) {
sd_journal_perror("open struct of properties failed."); sd_journal_perror("open struct of properties failed.");
return ret; return ret;
@ -195,7 +198,7 @@ int processKVPair(msg_ptr &msg, const std::map<std::string_view, std::string_vie
return ret; return ret;
} }
if (ret = appendPropValue(msg, getPropType(key), valueStr); ret < 0) { if (ret = appendPropValue(msg, getPropType(key), value); ret < 0) {
sd_journal_perror("append value of property failed."); sd_journal_perror("append value of property failed.");
return ret; return ret;
} }
@ -212,7 +215,7 @@ int processKVPair(msg_ptr &msg, const std::map<std::string_view, std::string_vie
std::string cmdParse(msg_ptr &msg, std::deque<std::string_view> cmdLines) std::string cmdParse(msg_ptr &msg, std::deque<std::string_view> cmdLines)
{ {
std::string serviceName{"internalError"}; std::string serviceName{"internalError"};
std::map<std::string_view, std::string_view> props; std::map<std::string_view, std::list<std::string_view>> props;
while (!cmdLines.empty()) { // NOTE: avoid stl exception while (!cmdLines.empty()) { // NOTE: avoid stl exception
auto str = cmdLines.front(); auto str = cmdLines.front();
if (str.size() < 2) { if (str.size() < 2) {
@ -249,7 +252,7 @@ std::string cmdParse(msg_ptr &msg, std::deque<std::string_view> cmdLines)
cmdLines.pop_front(); cmdLines.pop_front();
continue; continue;
} }
props[key] = kvStr.substr(splitIndex + 1); props[key].push_back(kvStr.substr(splitIndex + 1));
cmdLines.pop_front(); cmdLines.pop_front();
continue; continue;
} }
@ -265,18 +268,19 @@ std::string cmdParse(msg_ptr &msg, std::deque<std::string_view> cmdLines)
serviceName = "invalidInput"; serviceName = "invalidInput";
return serviceName; return serviceName;
} }
int ret;
if (props.find("unitName") == props.cend()) { if (props.find("unitName") == props.cend()) {
sd_journal_perror("unitName doesn't exists."); sd_journal_perror("unitName doesn't exists.");
serviceName = "invalidInput"; serviceName = "invalidInput";
return serviceName; return serviceName;
} }
if (ret = sd_bus_message_append(msg, "s", props["unitName"].data()); ret < 0) { // unitName
int ret;
if (ret = sd_bus_message_append(msg, "s", props["unitName"].front().data()); ret < 0) { // unitName
sd_journal_perror("append unitName failed."); sd_journal_perror("append unitName failed.");
return serviceName; return serviceName;
} }
serviceName = props["unitName"]; serviceName = props["unitName"].front();
props.erase("unitName"); props.erase("unitName");
if (ret = sd_bus_message_append(msg, "s", "replace"); ret < 0) { // start mode if (ret = sd_bus_message_append(msg, "s", "replace"); ret < 0) { // start mode

View File

@ -3,6 +3,7 @@
// SPDX-License-Identifier: LGPL-3.0-or-later // SPDX-License-Identifier: LGPL-3.0-or-later
#include "variantValue.h" #include "variantValue.h"
#include "constant.h"
#include <sstream> #include <sstream>
std::unique_ptr<VariantValue> creatValueHandler(msg_ptr &msg, DBusValueType type) std::unique_ptr<VariantValue> creatValueHandler(msg_ptr &msg, DBusValueType type)
@ -34,29 +35,21 @@ int StringValue::appendValue(std::string &&value) noexcept
int ASValue::openVariant() noexcept int ASValue::openVariant() noexcept
{ {
return sd_bus_message_open_container(msgRef(), SD_BUS_TYPE_VARIANT, "as"); if (int ret = sd_bus_message_open_container(msgRef(), SD_BUS_TYPE_VARIANT, "as"); ret < 0)
return ret;
return sd_bus_message_open_container(msgRef(), SD_BUS_TYPE_ARRAY, "s");
} }
int ASValue::closeVariant() noexcept int ASValue::closeVariant() noexcept
{ {
if (int ret = sd_bus_message_close_container(msgRef()); ret < 0)
return ret;
return sd_bus_message_close_container(msgRef()); return sd_bus_message_close_container(msgRef());
} }
int ASValue::appendValue(std::string &&value) noexcept int ASValue::appendValue(std::string &&value) noexcept
{ {
std::string envs{std::move(value)}; return sd_bus_message_append(msgRef(), "s", value.data());
std::istringstream stream{envs};
int ret{0};
if (ret = sd_bus_message_open_container(msgRef(), SD_BUS_TYPE_ARRAY, "s"); ret < 0) {
return ret;
}
for (std::string line; std::getline(stream, line, ';');) {
if (ret = sd_bus_message_append(msgRef(), "s", line.data()); ret < 0) {
return ret;
}
}
return sd_bus_message_close_container(msgRef());
} }

12
debian/changelog vendored
View File

@ -1,3 +1,15 @@
dde-application-manager (1.2.16) unstable; urgency=medium
* release 1.2.16
-- tsic404 <liuheng@deepin.org> Thu, 10 Oct 2024 15:34:38 +0800
dde-application-manager (1.2.15) unstable; urgency=medium
* release 1.2.15
-- Mike Chen <chenke@deepin.org> Wed, 10 Jul 2024 13:09:51 +0800
dde-application-manager (1.2.14) unstable; urgency=medium dde-application-manager (1.2.14) unstable; urgency=medium
* release 1.2.14 * release 1.2.14

View File

@ -1 +1,2 @@
usr/share/dde-application-manager/*.xml usr/share/dde-application-manager/*.xml
usr/lib/*/cmake/DDEApplicationManager/*

View File

@ -1,6 +1,6 @@
etc/dpkg/* etc/dpkg/*
usr/bin/* usr/bin/*
usr/lib/* usr/lib/systemd/*
usr/libexec/* usr/libexec/*
usr/share/dbus-1/* usr/share/dbus-1/*
usr/share/dsg/* usr/share/dsg/*

View File

@ -70,5 +70,7 @@ install(FILES ${CMAKE_CURRENT_LIST_DIR}/hooks.d/debFix.sh
) )
dtk_add_config_meta_files(APPID ${APPLICATION_SERVICEID} dtk_add_config_meta_files(APPID ${APPLICATION_SERVICEID}
FILES ${CMAKE_CURRENT_LIST_DIR}/dsg/configs/dde-application-manager/org.deepin.dde.am.json FILES
${CMAKE_CURRENT_LIST_DIR}/dsg/configs/dde-application-manager/org.deepin.dde.am.json
${CMAKE_CURRENT_LIST_DIR}/dsg/configs/dde-application-manager/org.deepin.dde.application-manager.json
) )

View File

@ -0,0 +1,26 @@
{
"magic": "dsg.config.meta",
"version": "1.0",
"contents": {
"appExtraEnvironments": {
"value": [],
"serial": 0,
"flags": [],
"name": "Launching app with extra environments",
"name[zh_CN]": "启动应用时附加额外环境变量",
"description": "Launching app with extra environments",
"permissions": "readwrite",
"visibility": "public"
},
"appEnvironmentsBlacklist": {
"value": [],
"serial": 0,
"flags": [],
"name": "Ignore blacklisted environment variables before launching app",
"name[zh_CN]": "启动应用时取消某些环境变量",
"description": "Ignore blacklisted environment variables before launching app",
"permissions": "readwrite",
"visibility": "public"
}
}
}

View File

@ -65,4 +65,8 @@ constexpr auto ApplicationManagerHookDir = u8"/deepin/dde-application-manager/ho
constexpr auto ApplicationManagerToolsConfig = u8"org.deepin.dde.am"; constexpr auto ApplicationManagerToolsConfig = u8"org.deepin.dde.am";
constexpr auto ApplicationManagerConfig = u8"org.deepin.dde.application-manager";
constexpr auto AppExtraEnvironments = u8"appExtraEnvironments";
constexpr auto AppEnvironmentsBlacklist = u8"appEnvironmentsBlacklist";
#endif #endif

View File

@ -24,6 +24,7 @@ target_link_libraries(
Qt${QT_VERSION_MAJOR}::Core Qt${QT_VERSION_MAJOR}::Core
Qt${QT_VERSION_MAJOR}::DBus Qt${QT_VERSION_MAJOR}::DBus
Qt${QT_VERSION_MAJOR}::Concurrent Qt${QT_VERSION_MAJOR}::Concurrent
Dtk6::Core
) )
target_include_directories( target_include_directories(

View File

@ -14,6 +14,7 @@
#include "launchoptions.h" #include "launchoptions.h"
#include "desktopentry.h" #include "desktopentry.h"
#include "desktopfileparser.h" #include "desktopfileparser.h"
#include "config.h"
#include <QUuid> #include <QUuid>
#include <QStringList> #include <QStringList>
#include <QList> #include <QList>
@ -31,22 +32,51 @@
#include <qtmetamacros.h> #include <qtmetamacros.h>
#include <utility> #include <utility>
#include <wordexp.h> #include <wordexp.h>
#include <DConfig>
static inline void appendEnvs(const QVariant &var, QStringList &envs)
{
if (var.canConvert<QStringList>()) {
envs.append(var.value<QStringList>());
} else if (var.canConvert<QString>()) {
envs.append(var.value<QString>().split(";", Qt::SkipEmptyParts));
}
}
void ApplicationService::appendExtraEnvironments(QVariantMap &runtimeOptions) const noexcept void ApplicationService::appendExtraEnvironments(QVariantMap &runtimeOptions) const noexcept
{ {
QStringList envs; DCORE_USE_NAMESPACE
QStringList envs, unsetEnvs;
const QString &env = environ(); const QString &env = environ();
if (!env.isEmpty()) if (!env.isEmpty())
envs.append(env); envs.append(env);
if (auto it = runtimeOptions.find("env"); it != runtimeOptions.cend()) { if (auto it = runtimeOptions.find("env"); it != runtimeOptions.cend()) {
envs.append(it->value<QString>()); appendEnvs(*it, envs);
}
if (auto it = runtimeOptions.find("unsetEnv"); it != runtimeOptions.cend()) {
appendEnvs(*it, unsetEnvs);
}
std::unique_ptr<DConfig> config(DConfig::create(ApplicationServiceID,
ApplicationManagerConfig,
QString("/%1").arg((id())))); // $appid as subpath
if (config->isValid()) {
const QStringList &extraEnvs = config->value(AppExtraEnvironments).toStringList();
if (!extraEnvs.isEmpty())
envs.append(extraEnvs);
const QStringList &envsBlacklist = config->value(AppEnvironmentsBlacklist).toStringList();
if (!envsBlacklist.isEmpty())
unsetEnvs.append(envsBlacklist);
} }
// it's useful for App to get itself AppId. // it's useful for App to get itself AppId.
envs.append(QString{"DSG_APP_ID=%1"}.arg(id())); envs.append(QString{"DSG_APP_ID=%1"}.arg(id()));
runtimeOptions.insert("env", envs.join(';')); runtimeOptions.insert("env", envs);
runtimeOptions.insert("unsetEnv", unsetEnvs);
} }
ApplicationService::ApplicationService(DesktopFile source, ApplicationService::ApplicationService(DesktopFile source,
@ -220,8 +250,8 @@ ApplicationService::Launch(const QString &action, const QStringList &fields, con
execStr = toString(actionExec.value()); execStr = toString(actionExec.value());
if (execStr.isEmpty()) { if (execStr.isEmpty()) {
qWarning() << "exec value to string failed, try default action."; // we need this log. qWarning() << "exec value to string failed, try default action."; // we need this log.
break;
} }
break; break;
} }
@ -265,7 +295,7 @@ ApplicationService::Launch(const QString &action, const QStringList &fields, con
if (terminal()) { if (terminal()) {
// don't change this sequence // don't change this sequence
execCmds.push_front("-C"); // means run a shellscript execCmds.push_front("-e"); // run all original execution commands in deepin-terminal
execCmds.push_front("--keep-open"); // keep terminal open, prevent exit immediately execCmds.push_front("--keep-open"); // keep terminal open, prevent exit immediately
execCmds.push_front("deepin-terminal"); execCmds.push_front("deepin-terminal");
} }
@ -274,46 +304,78 @@ ApplicationService::Launch(const QString &action, const QStringList &fields, con
auto &jobManager = parent()->jobManager(); auto &jobManager = parent()->jobManager();
return jobManager.addJob( return jobManager.addJob(
m_applicationPath.path(), m_applicationPath.path(),
[this, binary = std::move(bin), commands = std::move(cmds)](const QVariant &variantValue) -> QVariant { [this, binary = std::move(bin), commands = std::move(cmds)](const QVariant &value) -> QVariant {
auto resourceFile = variantValue.toString(); auto rawResources = value.toString();
auto instanceRandomUUID = QUuid::createUuid().toString(QUuid::Id128); auto instanceRandomUUID = QUuid::createUuid().toString(QUuid::Id128);
auto objectPath = m_applicationPath.path() + "/" + instanceRandomUUID; auto objectPath = m_applicationPath.path() + "/" + instanceRandomUUID;
auto newCommands = commands; auto newCommands = commands;
newCommands.push_front(QString{"--SourcePath=%1"}.arg(m_desktopSource.sourcePath())); newCommands.push_front(QString{"--SourcePath=%1"}.arg(m_desktopSource.sourcePath()));
auto location = newCommands.indexOf(R"(%f)"); if (rawResources.isEmpty()) {
if (location != -1) { // due to std::move, there only remove once
newCommands.remove(location);
}
if (resourceFile.isEmpty()) {
newCommands.push_front(QString{R"(--unitName=app-DDE-%1@%2.service)"}.arg( newCommands.push_front(QString{R"(--unitName=app-DDE-%1@%2.service)"}.arg(
escapeApplicationId(this->id()), instanceRandomUUID)); // launcher should use this instanceId escapeApplicationId(this->id()), instanceRandomUUID)); // launcher should use this instanceId
QProcess process; QProcess process;
qDebug() << "run with commands:" << newCommands; qDebug() << "launcher :" << m_launcher << "run with commands:" << newCommands;
process.start(m_launcher, newCommands); process.start(m_launcher, newCommands);
process.waitForFinished(); process.waitForFinished();
if (auto code = process.exitCode(); code != 0) { if (auto code = process.exitCode(); code != 0) {
qWarning() << "Launch Application Failed"; qWarning() << "Launch Application Failed";
return QDBusError::Failed; return QDBusError::Failed;
} }
return objectPath; return objectPath;
} }
auto url = QUrl::fromUserInput(resourceFile); auto location = newCommands.end();
if (!url.isValid()) { // if url is invalid, passing to launcher directly qsizetype fieldIndex{-1};
auto scheme = url.scheme(); for (auto it = newCommands.begin(); it != newCommands.end(); ++it) {
if (!scheme.isEmpty()) { auto fieldLocation = it->indexOf(R"(%f)");
// TODO: resourceFile = processRemoteFile(resourceFile); if (fieldLocation != -1) {
fieldIndex = fieldLocation;
location = it;
break;
}
fieldLocation = it->indexOf(R"(%F)");
if (fieldLocation != -1) {
fieldIndex = fieldLocation;
location = it;
break;
} }
} }
// NOTE: resourceFile must be available in the following contexts if (location == newCommands.end()) {
newCommands.insert(location, resourceFile); qCritical() << R"(internal logic error, can't find %f or %F in exec command, abort.)";
return QDBusError::Failed;
}
const auto &rawResource = rawResources.split(' ', Qt::SkipEmptyParts);
QStringList resources;
std::transform(rawResource.cbegin(), rawResource.cend(), std::back_inserter(resources), [](const QString &res) {
auto url = QUrl::fromUserInput(res);
if (url.isValid()) {
if (url.isLocalFile()) {
return url.toLocalFile();
}
// for now, we only support local file, maybe we will support remote file in the future.
// TODO: return processRemoteFile(url);
} // if url is invalid, passing to launcher directly
return res;
});
auto tmpRes = resources.join(' ');
location->replace(fieldIndex, tmpRes.size(), tmpRes);
auto newCmd = location->split(' ', Qt::SkipEmptyParts);
location = newCommands.erase(location);
for (auto &c : newCmd) {
location = newCommands.insert(location, std::move(c));
}
newCommands.push_front(QString{R"(--unitName=DDE-%1@%2.service)"}.arg(this->id(), instanceRandomUUID)); newCommands.push_front(QString{R"(--unitName=DDE-%1@%2.service)"}.arg(this->id(), instanceRandomUUID));
QProcess process; QProcess process;
qDebug() << "run with commands:" << newCommands; qDebug().noquote() << "launcher :" << m_launcher << "run with commands:" << newCommands;
process.start(getApplicationLauncherBinary(), newCommands); process.start(getApplicationLauncherBinary(), newCommands);
process.waitForFinished(); process.waitForFinished();
auto exitCode = process.exitCode(); auto exitCode = process.exitCode();
@ -321,6 +383,7 @@ ApplicationService::Launch(const QString &action, const QStringList &fields, con
qWarning() << "Launch Application Failed"; qWarning() << "Launch Application Failed";
return QDBusError::Failed; return QDBusError::Failed;
} }
return objectPath; return objectPath;
}, },
std::move(res)); std::move(res));
@ -529,6 +592,12 @@ bool ApplicationService::terminal() const noexcept
return false; return false;
} }
QString ApplicationService::startupWMClass() const noexcept
{
auto value = findEntryValue(DesktopFileEntryKey, "StartupWMClass", EntryValueType::String);
return value.isNull() ? QString{} : value.toString();
}
qint64 ApplicationService::installedTime() const noexcept qint64 ApplicationService::installedTime() const noexcept
{ {
return m_installedTime; return m_installedTime;
@ -599,6 +668,12 @@ bool ApplicationService::autostartCheck(const QString &filePath) const noexcept
} }
} }
QString source = s.value(DesktopFileEntryKey, X_Deepin_GenerateSource).value_or(DesktopEntry::Value{}).toString();
// file has been removed
if (source != m_autostartSource.m_filePath && filePath != m_autostartSource.m_filePath) {
return false;
}
auto hiddenVal = s.value(DesktopFileEntryKey, DesktopEntryHidden); auto hiddenVal = s.value(DesktopFileEntryKey, DesktopEntryHidden);
if (!hiddenVal) { if (!hiddenVal) {
qDebug() << "no hidden in autostart desktop"; qDebug() << "no hidden in autostart desktop";
@ -636,11 +711,6 @@ bool ApplicationService::isAutoStart() const noexcept
{"*.desktop"}, {"*.desktop"},
QDir::Name | QDir::DirsLast); QDir::Name | QDir::DirsLast);
// file has been removed
if (destDesktopFile != m_autostartSource.m_filePath) {
return false;
}
return autostartCheck(destDesktopFile); return autostartCheck(destDesktopFile);
} }
@ -650,7 +720,7 @@ void ApplicationService::setAutoStart(bool autostart) noexcept
return; return;
} }
QDir startDir (getAutoStartDirs().first()); QDir startDir(getAutoStartDirs().first());
if (!startDir.exists() && !startDir.mkpath(startDir.path())) { if (!startDir.exists() && !startDir.mkpath(startDir.path())) {
qWarning() << "mkpath " << startDir.path() << "failed"; qWarning() << "mkpath " << startDir.path() << "failed";
safe_sendErrorReply(QDBusError::InternalError); safe_sendErrorReply(QDBusError::InternalError);
@ -863,6 +933,7 @@ void ApplicationService::resetEntry(DesktopEntry *newEntry) noexcept
emit terminalChanged(); emit terminalChanged();
emit environChanged(); emit environChanged();
emit launchedTimesChanged(); emit launchedTimesChanged();
emit startupWMClassChanged();
} }
std::optional<QStringList> ApplicationService::unescapeExecArgs(const QString &str) noexcept std::optional<QStringList> ApplicationService::unescapeExecArgs(const QString &str) noexcept
@ -919,162 +990,236 @@ std::optional<QStringList> ApplicationService::unescapeExecArgs(const QString &s
return execList; return execList;
} }
LaunchTask ApplicationService::unescapeExec(const QString &str, const QStringList &fields) noexcept LaunchTask ApplicationService::unescapeExec(const QString &str, QStringList fields) noexcept
{ {
LaunchTask task; LaunchTask task;
auto opt = unescapeExecArgs(str); auto args = unescapeExecArgs(str);
if (!opt.has_value()) { if (!args) {
qWarning() << "unescapeExecArgs failed."; qWarning() << "unescapeExecArgs failed.";
return {}; return {};
} }
auto execList = std::move(opt).value(); if (args->isEmpty()) {
if (execList.isEmpty()) {
qWarning() << "exec format is invalid."; qWarning() << "exec format is invalid.";
return {}; return {};
} }
task.LaunchBin = execList.first(); auto processUrl = [](const QString &str) {
QRegularExpression re{"%[fFuUickdDnNvm]"}; auto url = QUrl::fromUserInput(str);
auto matcher = re.match(str); if (!url.isValid()) {
if (!matcher.hasMatch()) { qDebug() << "url is invalid, pass to exec directly.";
task.command.append(std::move(execList)); return str;
task.Resources.emplace_back(QString{""}); // mapReduce should run once at least }
return task;
}
auto list = matcher.capturedTexts(); if (url.isLocalFile()) {
if (list.count() != 1) { return url.toLocalFile();
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(); return url.toString();
auto codeStr = QString(R"(%%1)").arg(filesCode); };
auto location = execList.indexOf(codeStr);
if (location == -1) {
qWarning() << "invalid exec format, all filed code will be ignored.";
return {};
}
switch (filesCode) { task.LaunchBin = args->first();
case 'f': { // Defer to async job const QChar percentage{'%'};
task.command.append(std::move(execList)); bool exclusiveField{false};
for (const auto &field : fields) {
task.Resources.emplace_back(field); for (const auto &arg : *args) {
} QString newArg;
} break;
case 'u': { for (const auto *it = arg.cbegin(); it != arg.cend();) {
execList.removeAt(location); if (*it != percentage) {
if (fields.empty()) { newArg.append(*(it++));
task.command.append(execList); continue;
break;
}
if (fields.count() > 1) {
qDebug() << R"(fields count is greater than one, %u will only take first element.)";
}
execList.insert(location, fields.first());
task.command.append(execList);
} break;
case 'F': {
execList.remove(location);
auto it = execList.begin() + location;
for (const auto &field : fields) {
auto tmp = QUrl::fromUserInput(field);
if (auto scheme = tmp.scheme(); scheme.startsWith("file") or scheme.isEmpty()) {
it = execList.insert(it, tmp.toLocalFile());
} else {
qWarning() << "shouldn't replace %F with an URL:" << field;
it = execList.insert(it, field);
} }
++it;
} const auto *code = it + 1;
task.command.append(std::move(execList)); if (code == arg.cend()) {
} break; qWarning() << R"(content of exec is invalid, a unterminated % is detected.)";
case 'U': { return {};
execList.removeAt(location); }
auto it = execList.begin() + location;
for (const auto &field : fields) { if (*code == percentage) {
it = execList.insert(it, field); newArg.append(percentage);
++it; it += 2;
} continue;
task.command.append(std::move(execList)); }
} break;
case 'i': { switch (code->toLatin1()) {
execList.removeAt(location); case 'f': { // Defer to async job
auto val = m_entry->value(DesktopFileEntryKey, "Icon"); if (exclusiveField) {
if (!val) { qDebug() << R"(exclusive field is detected again, %f will be ignored.)";
qDebug() << R"(Application Icons can't be found. %i will be ignored.)"; break;
task.command.append(std::move(execList)); }
return task; exclusiveField = true;
if (fields.empty()) {
qDebug() << R"(fields is empty, %f will be ignored.)";
break;
}
if (fields.size() > 1) {
qDebug() << R"(fields count is greater than one, %f will only take first element.)";
}
task.Resources.emplace_back(fields.takeFirst());
newArg.append(R"(%f)");
} break;
case 'u': {
if (exclusiveField) {
qDebug() << R"(exclusive field is detected again, %f will be ignored.)";
break;
}
exclusiveField = true;
if (fields.empty()) {
qDebug() << "fields is empty, %u will be ignored.";
break;
}
if (fields.size() > 1) {
qDebug() << R"(fields count is greater than one, %u will only take first element.)";
}
newArg.append(processUrl(fields.takeFirst()));
} break;
case 'F': { // Defer to async job
if (exclusiveField) {
qDebug() << R"(exclusive field is detected again, %f will be ignored.)";
break;
}
exclusiveField = true;
task.Resources.emplace_back(fields.join(' '));
fields.clear();
newArg.append(R"(%F)");
} break;
case 'U': {
if (exclusiveField) {
qDebug() << R"(exclusive field is detected again, %f will be ignored.)";
break;
}
exclusiveField = true;
QStringList urls;
std::transform(fields.cbegin(), fields.cend(), std::back_inserter(urls), processUrl);
fields.clear();
newArg.append(urls.join(' ')); // split at the end of loop
} break;
case 'i': {
auto val = m_entry->value(DesktopFileEntryKey, "Icon");
if (!val) {
qDebug() << R"(Application Icons can't be found. %i will be ignored.)";
break;
}
auto iconStr = toIconString(val.value());
if (iconStr.isEmpty()) {
qDebug() << R"(Icons Convert to string failed. %i will be ignored.)";
break;
}
// split at the end of loop
newArg.append(QString{"--icon %1"}.arg(iconStr));
} break;
case 'c': {
auto val = m_entry->value(DesktopFileEntryKey, "Name");
if (!val) {
qDebug() << R"(Application Name can't be found. %c will be ignored.)";
break;
}
const auto &rawValue = val.value();
if (!rawValue.canConvert<QStringMap>()) {
qDebug() << "Name's underlying type mismatch:" << "QStringMap" << rawValue.metaType().name();
break;
}
auto NameStr = toLocaleString(rawValue.value<QStringMap>(), getUserLocale());
if (NameStr.isEmpty()) {
qDebug() << R"(Name Convert to locale string failed. %c will be ignored.)";
break;
}
newArg.append(NameStr);
} break;
case 'k': { // ignore all desktop file location for now.
newArg.append(m_desktopSource.sourcePath());
} break;
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': {
qDebug() << "field code" << *code << "has been deprecated.";
} break;
default: {
qDebug() << "unknown field code:" << *code << ", ignore it.";
}
}
it += 2; // skip filed code
} }
auto iconStr = toIconString(val.value()); auto newArgList = newArg.split(' ', Qt::SkipEmptyParts);
if (iconStr.isEmpty()) { if (!newArgList.isEmpty()) {
qDebug() << R"(Icons Convert to string failed. %i will be ignored.)"; task.command.append(std::move(newArgList));
task.command.append(std::move(execList));
return task;
} }
auto it = execList.insert(location, iconStr);
execList.insert(it, "--icon");
task.command.append(std::move(execList));
} break;
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.)";
task.command.append(std::move(execList));
return task;
}
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()) {
qDebug() << R"(Name Convert to locale string failed. %c will be ignored.)";
task.command.append(std::move(execList));
return task;
}
execList.insert(location, NameStr);
task.command.append(std::move(execList));
} break;
case 'k': { // ignore all desktop file location for now.
execList.removeAt(location);
task.command.append(std::move(execList));
} break;
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));
} break;
default: {
qDebug() << "unrecognized file code.";
}
} }
if (task.Resources.isEmpty()) { if (task.Resources.isEmpty()) {
task.Resources.emplace_back(QString{""}); // mapReduce should run once at least task.Resources.emplace_back(QString{""}); // mapReduce should run once at least
} }
qInfo() << "after unescape exec:" << task.LaunchBin << task.command << task.Resources;
return task; return task;
} }
void ApplicationService::unescapeEens(QVariantMap &options) noexcept
{
if (options.find("env") == options.end()) {
return;
}
QStringList result;
auto envs = options["env"];
for (const QString &var : envs.toStringList()) {
wordexp_t p;
if (wordexp(var.toStdString().c_str(), &p, 0) == 0) {
for (size_t i = 0; i < p.we_wordc; i++) {
result << QString::fromLocal8Bit(p.we_wordv[i]); // 将结果转换为QString
}
wordfree(&p);
} else {
return;
}
}
options.insert("env", result);
}
void ApplicationService::autoRemoveFromDesktop() const noexcept
{
auto dir = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
if (dir.isEmpty()) {
return;
}
QFileInfo desktopFile{QDir{dir}.filePath(m_desktopSource.desktopId() + ".desktop")};
if (!desktopFile.isSymbolicLink()) {
qDebug() << desktopFile.filePath() << " is not symbolicLink";
return;
}
QFile file{desktopFile.filePath()};
auto success = file.remove();
if (!success) {
qWarning() << "remove desktop file failed:" << file.errorString();
return;
}
}
QVariant ApplicationService::findEntryValue(const QString &group, QVariant ApplicationService::findEntryValue(const QString &group,
const QString &valueKey, const QString &valueKey,
EntryValueType type, EntryValueType type,

View File

@ -74,6 +74,9 @@ public:
Q_PROPERTY(bool Terminal READ terminal NOTIFY terminalChanged) Q_PROPERTY(bool Terminal READ terminal NOTIFY terminalChanged)
[[nodiscard]] bool terminal() const noexcept; [[nodiscard]] bool terminal() const noexcept;
Q_PROPERTY(QString StartupWMClass READ startupWMClass NOTIFY startupWMClassChanged)
[[nodiscard]] QString startupWMClass() const noexcept;
// FIXME: // FIXME:
// This property should implement with fuse guarded // This property should implement with fuse guarded
// $XDG_CONFIG_HOME/autostart/. Current implementation has some problems, // $XDG_CONFIG_HOME/autostart/. Current implementation has some problems,
@ -131,7 +134,9 @@ public:
EntryValueType type, EntryValueType type,
const QLocale &locale = getUserLocale()) const noexcept; const QLocale &locale = getUserLocale()) const noexcept;
[[nodiscard]] LaunchTask unescapeExec(const QString &str, const QStringList &fields) noexcept; [[nodiscard]] LaunchTask unescapeExec(const QString &str, QStringList fields) noexcept;
void autoRemoveFromDesktop() const noexcept;
void unescapeEens(QVariantMap&) noexcept;
[[nodiscard]] static std::optional<QStringList> unescapeExecArgs(const QString &str) noexcept; [[nodiscard]] static std::optional<QStringList> unescapeExecArgs(const QString &str) noexcept;
public Q_SLOTS: public Q_SLOTS:
@ -163,6 +168,7 @@ Q_SIGNALS:
void terminalChanged(); void terminalChanged();
void environChanged(); void environChanged();
void launchedTimesChanged(); void launchedTimesChanged();
void startupWMClassChanged();
private: private:
friend class ApplicationManager1Service; friend class ApplicationManager1Service;

View File

@ -544,6 +544,8 @@ template <typename Key, typename Value>
ObjectMap dumpDBusObject(const QHash<Key, QSharedPointer<Value>> &map) ObjectMap dumpDBusObject(const QHash<Key, QSharedPointer<Value>> &map)
{ {
static_assert(std::is_base_of_v<QObject, Value>, "dumpDBusObject only support which derived by QObject class"); static_assert(std::is_base_of_v<QObject, Value>, "dumpDBusObject only support which derived by QObject class");
static_assert(std::is_same_v<Key, QString> || std::is_same_v<Key, QDBusObjectPath>,
"dumpDBusObject only support QString/QDBusObject as key type");
ObjectMap objs; ObjectMap objs;
for (const auto &[key, value] : map.asKeyValueRange()) { for (const auto &[key, value] : map.asKeyValueRange()) {
@ -552,8 +554,6 @@ ObjectMap dumpDBusObject(const QHash<Key, QSharedPointer<Value>> &map)
objs.insert(QDBusObjectPath{getObjectPathFromAppId(key)}, interAndProps); objs.insert(QDBusObjectPath{getObjectPathFromAppId(key)}, interAndProps);
} else if constexpr (std::is_same_v<Key, QDBusObjectPath>) { } else if constexpr (std::is_same_v<Key, QDBusObjectPath>) {
objs.insert(key, interAndProps); objs.insert(key, interAndProps);
} else {
static_assert(false, "dumpDBusObject only support QString/QDBusObject as key type");
} }
} }

View File

@ -17,6 +17,8 @@ QStringList generateCommand(const QVariantMap &props) noexcept
options.emplace_back(std::make_unique<setUserLaunchOption>(value)); options.emplace_back(std::make_unique<setUserLaunchOption>(value));
} else if (key == setEnvLaunchOption::key()) { } else if (key == setEnvLaunchOption::key()) {
options.emplace_back(std::make_unique<setEnvLaunchOption>(value)); options.emplace_back(std::make_unique<setEnvLaunchOption>(value));
} else if (key == unsetEnvLaunchOption::key()) {
options.emplace_back(std::make_unique<unsetEnvLaunchOption>(value));
} else if (key == hookLaunchOption::key()) { } else if (key == hookLaunchOption::key()) {
options.emplace_back(std::make_unique<hookLaunchOption>(value)); options.emplace_back(std::make_unique<hookLaunchOption>(value));
} else if (key == setWorkingPathLaunchOption::key()) { } else if (key == setWorkingPathLaunchOption::key()) {
@ -100,16 +102,6 @@ QStringList splitLaunchOption::generateCommandLine() const noexcept
return QStringList{m_val.toString()}; return QStringList{m_val.toString()};
} }
QStringList setEnvLaunchOption::generateCommandLine() const noexcept
{
auto str = m_val.toString();
if (str.isEmpty()) {
return {};
}
return QStringList{QString{"--Environment=%1"}.arg(str)};
}
QStringList setWorkingPathLaunchOption::generateCommandLine() const noexcept QStringList setWorkingPathLaunchOption::generateCommandLine() const noexcept
{ {
auto str = m_val.toString(); auto str = m_val.toString();
@ -120,13 +112,18 @@ QStringList setWorkingPathLaunchOption::generateCommandLine() const noexcept
return QStringList{QString{"--WorkingDirectory=%1"}.arg(str)}; return QStringList{QString{"--WorkingDirectory=%1"}.arg(str)};
} }
QStringList builtInSearchExecOption::generateCommandLine() const noexcept QStringList StringListLaunchOption::generateCommandLine() const noexcept
{ {
auto list = m_val.toStringList(); auto list = m_val.toStringList();
if (list.isEmpty()) { if (list.isEmpty()) {
return {}; return {};
} }
auto content = list.join(';'); QStringList ret;
return QStringList{QString{"--ExecSearchPath=%1"}.arg(content)}; const QString ok = optionKey();
for (const auto &ov : list) {
ret << QString{"%1=%2"}.arg(ok).arg(ov);
}
return ret;
} }

View File

@ -27,6 +27,14 @@ protected:
LaunchOption() = default; LaunchOption() = default;
}; };
struct StringListLaunchOption : public LaunchOption
{
using LaunchOption::LaunchOption;
[[nodiscard]] QStringList generateCommandLine() const noexcept override;
protected:
[[nodiscard]] virtual const QString optionKey() const noexcept = 0;
};
struct setUserLaunchOption : public LaunchOption struct setUserLaunchOption : public LaunchOption
{ {
using LaunchOption::LaunchOption; using LaunchOption::LaunchOption;
@ -43,9 +51,9 @@ struct setUserLaunchOption : public LaunchOption
[[nodiscard]] QStringList generateCommandLine() const noexcept override; [[nodiscard]] QStringList generateCommandLine() const noexcept override;
}; };
struct setEnvLaunchOption : public LaunchOption struct setEnvLaunchOption : public StringListLaunchOption
{ {
using LaunchOption::LaunchOption; using StringListLaunchOption::StringListLaunchOption;
[[nodiscard]] const QString &type() const noexcept override [[nodiscard]] const QString &type() const noexcept override
{ {
static QString tp{systemdOption}; static QString tp{systemdOption};
@ -56,7 +64,10 @@ struct setEnvLaunchOption : public LaunchOption
static QString env{"env"}; static QString env{"env"};
return env; return env;
} }
[[nodiscard]] QStringList generateCommandLine() const noexcept override; protected:
[[nodiscard]] virtual const QString optionKey() const noexcept {
return QString("--Environment");
}
}; };
struct splitLaunchOption : public LaunchOption struct splitLaunchOption : public LaunchOption
@ -110,9 +121,9 @@ struct setWorkingPathLaunchOption : public LaunchOption
[[nodiscard]] QStringList generateCommandLine() const noexcept override; [[nodiscard]] QStringList generateCommandLine() const noexcept override;
}; };
struct builtInSearchExecOption : public LaunchOption struct builtInSearchExecOption : public StringListLaunchOption
{ {
using LaunchOption::LaunchOption; using StringListLaunchOption::StringListLaunchOption;
[[nodiscard]] const QString &type() const noexcept override [[nodiscard]] const QString &type() const noexcept override
{ {
static QString tp{systemdOption}; static QString tp{systemdOption};
@ -123,7 +134,29 @@ struct builtInSearchExecOption : public LaunchOption
static QString key{"_builtIn_searchExec"}; static QString key{"_builtIn_searchExec"};
return key; return key;
} }
[[nodiscard]] QStringList generateCommandLine() const noexcept override; protected:
[[nodiscard]] virtual const QString optionKey() const noexcept {
return QString("--ExecSearchPath");
}
};
struct unsetEnvLaunchOption : public StringListLaunchOption
{
using StringListLaunchOption::StringListLaunchOption;
[[nodiscard]] const QString &type() const noexcept override
{
static QString tp{systemdOption};
return tp;
}
[[nodiscard]] static const QString &key() noexcept
{
static QString env{"unsetEnv"};
return env;
}
protected:
[[nodiscard]] virtual const QString optionKey() const noexcept {
return QString("--UnsetEnvironment");
}
}; };
QStringList generateCommand(const QVariantMap &props) noexcept; QStringList generateCommand(const QVariantMap &props) noexcept;