From accc9c2aacbe7bc7ab473b1302ce03f368cb6e7b Mon Sep 17 00:00:00 2001 From: Chen Linxuan Date: Tue, 7 Mar 2023 10:52:06 +0800 Subject: [PATCH] fix: introduce new DesktopFile class KeyFile::saveToFile not guaranteed section order, which is not the right behavior of desktop file. XDG requires the first section of desktop file always to be "Desktop Entry". So I write a new DesktopFile class, which override the saveToFile method of KeyFile, to make it work. close linuxdeepin/developer-center#3807 Signed-off-by: black-desk --- src/lib/desktopfile.cpp | 42 ++++++++++++++++++++++ src/lib/desktopfile.h | 24 +++++++++++++ src/lib/desktopinfo.cpp | 44 +++++++++++------------ src/lib/desktopinfo.h | 6 ++-- src/lib/keyfile.cpp | 24 +++++++++---- src/lib/keyfile.h | 10 +++--- src/modules/dock/appinfo.cpp | 2 +- src/modules/launcher/launcher.cpp | 18 +++++----- src/modules/startmanager/startmanager.cpp | 4 +-- 9 files changed, 127 insertions(+), 47 deletions(-) create mode 100644 src/lib/desktopfile.cpp create mode 100644 src/lib/desktopfile.h diff --git a/src/lib/desktopfile.cpp b/src/lib/desktopfile.cpp new file mode 100644 index 0000000..361f979 --- /dev/null +++ b/src/lib/desktopfile.cpp @@ -0,0 +1,42 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "desktopfile.h" + +DesktopFile::DesktopFile(char separtor) + : KeyFile(separtor) +{ +} + +DesktopFile::~DesktopFile() = default; + +bool DesktopFile::saveToFile(const std::string &filePath){ + FILE *sfp = fopen(filePath.data(), "w+"); + if (!sfp) { + perror("open file failed..."); + return false; + } + + // NOTE(black_desk): XDG require the first section of desktop file + // is always "Desktop Entry" + + auto mainSection = m_mainKeyMap.find("Desktop Entry"); + if (mainSection != m_mainKeyMap.end()) { + // FIXME(black_desk): should handle write fail. + writeSectionToFile(mainSection->first, mainSection->second, sfp); + } else { + // FIXME(black_desk): should have some warning. + } + + for (const auto &im : m_mainKeyMap) { + if (im.first == "Desktop Entry") { + continue; + } + // FIXME(black_desk): should handle write fail. + writeSectionToFile(im.first, im.second, sfp); + } + + fclose(sfp); + return true; +} diff --git a/src/lib/desktopfile.h b/src/lib/desktopfile.h new file mode 100644 index 0000000..f1ec748 --- /dev/null +++ b/src/lib/desktopfile.h @@ -0,0 +1,24 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: GPL-3.0-or-later + +#ifndef DESKTOPFILE_H +#define DESKTOPFILE_H + +#include "keyfile.h" + +#include +#include +#include + +// 解析desktop文件类 +class DesktopFile: public KeyFile +{ +public: + explicit DesktopFile(char separtor = ';'); + virtual ~DesktopFile(); + + virtual bool saveToFile(const std::string &filePath) override; +}; + +#endif // DESKTOPFILE_H diff --git a/src/lib/desktopinfo.cpp b/src/lib/desktopinfo.cpp index 34f7285..d9fc0d4 100644 --- a/src/lib/desktopinfo.cpp +++ b/src/lib/desktopinfo.cpp @@ -20,7 +20,7 @@ std::vector DesktopInfo::currentDesktops; DesktopInfo::DesktopInfo(const std::string &_fileName) : m_isValid(true) - , m_keyFile(KeyFile()) + , m_desktopFile() { std::string fileNameWithSuffix(_fileName); if (!DString::endWith(_fileName, ".desktop")) @@ -45,10 +45,10 @@ DesktopInfo::DesktopInfo(const std::string &_fileName) } } - m_keyFile.loadFile(m_fileName); + m_desktopFile.loadFile(m_fileName); // check DesktopInfo valid - std::vector mainKeys = m_keyFile.getMainKeys(); + std::vector mainKeys = m_desktopFile.getMainKeys(); if (mainKeys.size() == 0) m_isValid = false; @@ -58,11 +58,11 @@ DesktopInfo::DesktopInfo(const std::string &_fileName) if (!found) m_isValid = false; - if (m_keyFile.getStr(MainSection, KeyType) != TypeApplication) + if (m_desktopFile.getStr(MainSection, KeyType) != TypeApplication) m_isValid = false; - m_name = m_keyFile.getLocaleStr(MainSection, KeyName, ""); - m_icon = m_keyFile.getStr(MainSection, KeyIcon); + m_name = m_desktopFile.getLocaleStr(MainSection, KeyName, ""); + m_icon = m_desktopFile.getStr(MainSection, KeyIcon); m_id = getId(); } @@ -98,12 +98,12 @@ bool DesktopInfo::shouldShow() bool DesktopInfo::getNoDisplay() { - return m_keyFile.getBool(MainSection, KeyNoDisplay); + return m_desktopFile.getBool(MainSection, KeyNoDisplay); } bool DesktopInfo::getIsHidden() { - return m_keyFile.getBool(MainSection, KeyHidden); + return m_desktopFile.getBool(MainSection, KeyHidden); } bool DesktopInfo::getShowIn(std::vector desktopEnvs) @@ -119,8 +119,8 @@ bool DesktopInfo::getShowIn(std::vector desktopEnvs) desktopEnvs.assign(currentDesktops.begin(), currentDesktops.end()); } - std::vector onlyShowIn = m_keyFile.getStrList(MainSection, KeyOnlyShowIn); - std::vector notShowIn = m_keyFile.getStrList(MainSection, KeyNotShowIn); + std::vector onlyShowIn = m_desktopFile.getStrList(MainSection, KeyOnlyShowIn); + std::vector notShowIn = m_desktopFile.getStrList(MainSection, KeyNotShowIn); #ifdef QT_DEBUG auto strVector2qstrVector = [](const std::vector &vector) { @@ -159,7 +159,7 @@ bool DesktopInfo::getShowIn(std::vector desktopEnvs) std::string DesktopInfo::getExecutable() { - return m_keyFile.getStr(MainSection, KeyExec); + return m_desktopFile.getStr(MainSection, KeyExec); } bool DesktopInfo::isExecutableOk() @@ -213,12 +213,12 @@ bool DesktopInfo::isDesktopAction(std::string name) std::vector DesktopInfo::getActions() { std::vector actions; - for (const auto &mainKey : m_keyFile.getMainKeys()) { + for (const auto &mainKey : m_desktopFile.getMainKeys()) { if (DString::startWith(mainKey, "Desktop Action") || DString::endWith(mainKey, "Shortcut Group")) { DesktopAction action; - action.name = m_keyFile.getLocaleStr(mainKey, KeyName, ""); - action.exec = m_keyFile.getStr(mainKey, KeyExec); + action.name = m_desktopFile.getLocaleStr(mainKey, KeyName, ""); + action.exec = m_desktopFile.getStr(mainKey, KeyExec); action.section = mainKey; actions.push_back(action); } @@ -246,13 +246,13 @@ DesktopInfo DesktopInfo::getDesktopInfoById(std::string appId) bool DesktopInfo::getTerminal() { - return m_keyFile.getBool(MainSection, KeyTerminal); + return m_desktopFile.getBool(MainSection, KeyTerminal); } // TryExec is Path to an executable file on disk used to determine if the program is actually installed std::string DesktopInfo::getTryExec() { - return m_keyFile.getStr(MainSection, KeyTryExec); + return m_desktopFile.getStr(MainSection, KeyTryExec); } // 按$PATH路径查找执行文件 @@ -301,7 +301,7 @@ std::string DesktopInfo::getId() std::string DesktopInfo::getGenericName() { - return m_keyFile.getLocaleStr(MainSection, KeyGenericName, ""); + return m_desktopFile.getLocaleStr(MainSection, KeyGenericName, ""); } std::string DesktopInfo::getName() @@ -316,17 +316,17 @@ std::string DesktopInfo::getIcon() std::string DesktopInfo::getCommandLine() { - return m_keyFile.getStr(MainSection, KeyExec); + return m_desktopFile.getStr(MainSection, KeyExec); } std::vector DesktopInfo::getKeywords() { - return m_keyFile.getLocaleStrList(MainSection, KeyKeywords, ""); + return m_desktopFile.getLocaleStrList(MainSection, KeyKeywords, ""); } std::vector DesktopInfo::getCategories() { - return m_keyFile.getStrList(MainSection, KeyCategories); + return m_desktopFile.getStrList(MainSection, KeyCategories); } void DesktopInfo::setDesktopOverrideExec(const std::string &execStr) @@ -334,9 +334,9 @@ void DesktopInfo::setDesktopOverrideExec(const std::string &execStr) m_overRideExec = execStr; } -KeyFile *DesktopInfo::getKeyFile() +DesktopFile *DesktopInfo::getDesktopFile() { - return &m_keyFile; + return &m_desktopFile; } // class AppsDir diff --git a/src/lib/desktopinfo.h b/src/lib/desktopinfo.h index 4c6871e..98e58c4 100644 --- a/src/lib/desktopinfo.h +++ b/src/lib/desktopinfo.h @@ -5,7 +5,7 @@ #ifndef DESKTOPINFO_H #define DESKTOPINFO_H -#include "keyfile.h" +#include "desktopfile.h" #include #include @@ -82,7 +82,7 @@ public: std::vector getCategories(); void setDesktopOverrideExec(const std::string &execStr); - KeyFile *getKeyFile(); + DesktopFile *getDesktopFile(); private: std::string getTryExec(); @@ -96,7 +96,7 @@ private: std::string m_icon; std::string m_overRideExec; bool m_isValid; - KeyFile m_keyFile; + DesktopFile m_desktopFile; }; // 应用目录类 diff --git a/src/lib/keyfile.cpp b/src/lib/keyfile.cpp index 870583e..f9f4186 100644 --- a/src/lib/keyfile.cpp +++ b/src/lib/keyfile.cpp @@ -177,12 +177,8 @@ bool KeyFile::saveToFile(const std::string &filePath) } for (const auto &im : m_mainKeyMap) { - const auto &keyMap = im.second; - std::string section = "[" + im.first + "]\n"; - fputs(section.c_str(), sfp); - for (const auto &ik : keyMap) { - std::string kv = ik.first + "=" + ik.second + "\n"; - fputs(kv.c_str(), sfp); + if (!writeSectionToFile(im.first, im.second,sfp)){ + return false; } } @@ -190,6 +186,22 @@ bool KeyFile::saveToFile(const std::string &filePath) return true; } +bool KeyFile::writeSectionToFile(const std::string& sectionName, const KeyMap& keyMap, FILE * file){ + if (file == nullptr) { + return false; + } + + std::string section = "[" + sectionName + "]\n"; + fputs(section.c_str(), file); + for (const auto &ik : keyMap) { + std::string kv = ik.first + "=" + ik.second + "\n"; + fputs(kv.c_str(), file); + } + + // FIXME(black_desk): should handle fputs error + return true; +} + bool KeyFile::loadFile(const std::string &filePath) { m_mainKeyMap.clear(); diff --git a/src/lib/keyfile.h b/src/lib/keyfile.h index 49d854c..0dbf238 100644 --- a/src/lib/keyfile.h +++ b/src/lib/keyfile.h @@ -12,12 +12,12 @@ typedef std::map KeyMap; typedef std::map MainKeyMap; -// 解析ini、desktop文件类 +// 解析ini文件类 class KeyFile { public: explicit KeyFile(char separtor = ';'); - ~KeyFile(); + virtual ~KeyFile(); bool getBool(const std::string §ion, const std::string &key, bool defaultValue = false); void setBool(const std::string §ion, const std::string &key, const std::string &defaultValue = "false"); @@ -34,7 +34,7 @@ public: std::vector getLocaleStrList(const std::string §ion, const std::string &key, std::string defaultLocale = ""); void setKey(const std::string §ion, const std::string &key, const std::string &value); - bool saveToFile(const std::string &filePath); + virtual bool saveToFile(const std::string &filePath); bool loadFile(const std::string &filePath); std::vector getMainKeys(); std::string getFilePath() @@ -45,12 +45,14 @@ public: // for test void print(); -private: +protected: MainKeyMap m_mainKeyMap; // section -> key : value std::string m_filePath; FILE *m_fp; bool m_modified; char m_listSeparator; + + static bool writeSectionToFile(const std::string& sectionName, const KeyMap& keyMap, FILE * file); }; #endif // KEYFILE_H diff --git a/src/modules/dock/appinfo.cpp b/src/modules/dock/appinfo.cpp index 98de79c..0e90b28 100644 --- a/src/modules/dock/appinfo.cpp +++ b/src/modules/dock/appinfo.cpp @@ -29,7 +29,7 @@ void AppInfo::init(DesktopInfo &info) return; } - std::string xDeepinVendor= info.getKeyFile()->getStr(MainSection, "X-Deepin-Vendor"); + std::string xDeepinVendor= info.getDesktopFile()->getStr(MainSection, "X-Deepin-Vendor"); if (xDeepinVendor == "deepin") { m_name = info.getGenericName().c_str(); if (m_name.isEmpty()) diff --git a/src/modules/launcher/launcher.cpp b/src/modules/launcher/launcher.cpp index c69ad53..4b09996 100644 --- a/src/modules/launcher/launcher.cpp +++ b/src/modules/launcher/launcher.cpp @@ -247,9 +247,9 @@ bool Launcher::requestSendToDesktop(QString appId) // 创建桌面快捷方式文件 DesktopInfo dinfo(itemsMap[appId].info.path.toStdString()); - dinfo.getKeyFile()->setKey(MainSection, dbusService.toStdString(), "X-Deepin-CreatedBy"); - dinfo.getKeyFile()->setKey(MainSection, appId.toStdString(), "X-Deepin-AppID"); - if (!dinfo.getKeyFile()->saveToFile(filePath.toStdString())) + dinfo.getDesktopFile()->setKey(MainSection, dbusService.toStdString(), "X-Deepin-CreatedBy"); + dinfo.getDesktopFile()->setKey(MainSection, appId.toStdString(), "X-Deepin-AppID"); + if (!dinfo.getDesktopFile()->saveToFile(filePath.toStdString())) return false; // 播放音频 @@ -907,7 +907,7 @@ AppType Launcher::getAppType(DesktopInfo &info, const Item &item) QFileInfo fileInfo; // 判断是否为flatpak应用 do { - if (!info.getKeyFile()->containKey(MainSection, "X-Flatpak")) + if (!info.getDesktopFile()->containKey(MainSection, "X-Flatpak")) break; std::vector parts = DString::splitStr(info.getCommandLine(), ' '); @@ -946,7 +946,7 @@ AppType Launcher::getAppType(DesktopInfo &info, const Item &item) // 判断是否为wineApp do { - std::string createdBy = info.getKeyFile()->getStr(MainSection, "X-Created-By"); + std::string createdBy = info.getDesktopFile()->getStr(MainSection, "X-Created-By"); if (DString::startWith(createdBy, "cxoffice-") || strstr(info.getCommandLine().c_str(), "env WINEPREFIX=")) { ty = AppType::WineApp; goto end; @@ -1184,10 +1184,10 @@ bool Launcher::isDeepinCustomDesktopFile(QString fileName) Item Launcher::NewItemWithDesktopInfo(DesktopInfo &info) { - QString enName(info.getKeyFile()->getStr(MainSection, KeyName).c_str()); - QString enComment(info.getKeyFile()->getStr(MainSection, KeyComment).c_str()); - QString xDeepinCategory(info.getKeyFile()->getStr(MainSection, "X-Deepin-Category").c_str()); - QString xDeepinVendor(info.getKeyFile()->getStr(MainSection, "X-Deepin-Vendor").c_str()); + QString enName(info.getDesktopFile()->getStr(MainSection, KeyName).c_str()); + QString enComment(info.getDesktopFile()->getStr(MainSection, KeyComment).c_str()); + QString xDeepinCategory(info.getDesktopFile()->getStr(MainSection, "X-Deepin-Category").c_str()); + QString xDeepinVendor(info.getDesktopFile()->getStr(MainSection, "X-Deepin-Vendor").c_str()); QString appName; if (xDeepinVendor == "deepin") diff --git a/src/modules/startmanager/startmanager.cpp b/src/modules/startmanager/startmanager.cpp index 8010543..1dc704a 100644 --- a/src/modules/startmanager/startmanager.cpp +++ b/src/modules/startmanager/startmanager.cpp @@ -343,7 +343,7 @@ bool StartManager::doLaunchAppWithOptions(QString desktopFile, uint32_t timestam } if (options.find("path") != options.end()) { - info.getKeyFile()->setKey(MainSection, KeyPath, options["path"].toString().toStdString()); + info.getDesktopFile()->setKey(MainSection, KeyPath, options["path"].toString().toStdString()); } if (options.find("desktop-override-exec") != options.end()) { @@ -433,7 +433,7 @@ bool StartManager::launch(DesktopInfo *info, QString cmdLine, uint32_t timestamp exeArgs.insert(0, SETTING->getDefaultTerminalExec()); } - std::string workingDir = info->getKeyFile()->getStr(MainSection, KeyPath); + std::string workingDir = info->getDesktopFile()->getStr(MainSection, KeyPath); if (workingDir.empty()) { workingDir = BaseDir::homeDir(); }