/* * Copyright (C) 2021 ~ 2022 Deepin Technology Co., Ltd. * * Author: weizhixiang * * Maintainer: weizhixiang * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "desktopinfo.h" #include "locale.h" #include "unistd.h" #include "dstring.h" #include "dfile.h" #include "basedir.h" #include #include #include #include std::vector DesktopInfo::currentDesktops; DesktopInfo::DesktopInfo(const std::string &_fileName) : m_isValid(true) , m_keyFile(KeyFile()) { std::string fileNameWithSuffix(_fileName); if (!DString::endWith(_fileName, ".desktop")) fileNameWithSuffix += ".desktop"; m_fileName = fileNameWithSuffix; if (DFile::dir(m_fileName).empty()) { // fileName是文件名,增加目录 bool isExisted = false; for (const auto &dir : BaseDir::appDirs()) { m_fileName = dir + fileNameWithSuffix; if (DFile::isExisted(m_fileName)) { isExisted = true; break; } } if (!isExisted) { m_isValid = false; return; } } m_keyFile.loadFile(m_fileName); // check DesktopInfo valid std::vector mainKeys = m_keyFile.getMainKeys(); if (mainKeys.size() == 0) m_isValid = false; bool found = std::any_of(mainKeys.begin(), mainKeys.end(), [](const auto &key) {return key == MainSection;}); if (!found) m_isValid = false; if (m_keyFile.getStr(MainSection, KeyType) != TypeApplication) m_isValid = false; m_name = m_keyFile.getLocaleStr(MainSection, KeyName, ""); m_icon = m_keyFile.getStr(MainSection, KeyIcon); m_id = getId(); } DesktopInfo::~DesktopInfo() { } std::string DesktopInfo::getFileName() { return m_fileName; } bool DesktopInfo::isValidDesktop() { return m_isValid; } bool DesktopInfo::shouldShow() { if (getNoDisplay() || getIsHidden()) return false; std::vector desktopEnvs; return getShowIn(desktopEnvs); } bool DesktopInfo::getNoDisplay() { return m_keyFile.getBool(MainSection, KeyNoDisplay); } bool DesktopInfo::getIsHidden() { return m_keyFile.getBool(MainSection, KeyHidden); } bool DesktopInfo::getShowIn(std::vector desktopEnvs) { if (desktopEnvs.size() == 0) { if (currentDesktops.size() == 0) { const char *env = getenv(envDesktopEnv.c_str()); const auto &desktop = DString::splitChars(env, ':'); currentDesktops.assign(desktop.begin(), desktop.end()); } desktopEnvs.assign(currentDesktops.begin(), currentDesktops.end()); } std::vector onlyShowIn = m_keyFile.getStrList(MainSection, KeyOnlyShowIn); std::vector notShowIn = m_keyFile.getStrList(MainSection, KeyNotShowIn); for (const auto &desktop : desktopEnvs) { bool ret = std::any_of(onlyShowIn.begin(), onlyShowIn.end(), [&desktop](const auto &d) {return d == desktop;}); if (ret) return true; ret = std::any_of(notShowIn.begin(), notShowIn.end(), [&desktop](const auto &d) {return d == desktop;}); if (ret) return false; } return onlyShowIn.size() == 0; } std::string DesktopInfo::getExecutable() { return m_keyFile.getStr(MainSection, KeyExec); } bool DesktopInfo::isExecutableOk() { // 检查TryExec字段 std::string value = getTryExec(); std::vector parts = DString::splitStr(value, ' '); if (parts.size() > 0 ) { value.assign(parts[0]); DString::delQuote(value); if (strstr(value.c_str(), "/") && DFile::isExisted(value)) return true; else return findExecutable(value); } // 检查Exec字段 value.assign(getExecutable()); parts.clear(); parts = DString::splitStr(value, ' '); if (parts.size() > 0) { value.assign(parts[0]); DString::delQuote(value); if (strstr(value.c_str(), "/") && DFile::isExisted(value)) return true; else return findExecutable(value); } return false; } bool DesktopInfo::isInstalled() { const char *name = m_fileName.c_str(); const char *found = strstr(name, "/applications/"); if (!found) return false; auto appDirs = BaseDir::appDirs(); return std::any_of(appDirs.begin(), appDirs.end(), [&name, &found] (std::string dir) -> bool {return strneq(dir.c_str(), name, size_t(found - name));}); } // [Desktop Action new-window] or [Full_Screenshot Shortcut Group] bool DesktopInfo::isDesktopAction(std::string name) { return DString::startWith(name.c_str(), "Desktop Action") || DString::endWith(name.c_str(), "Shortcut Group"); } std::vector DesktopInfo::getActions() { std::vector actions; for (const auto &mainKey : m_keyFile.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); actions.push_back(action); } } return actions; } // 使用appId获取DesktopInfo需检查有效性 DesktopInfo DesktopInfo::getDesktopInfoById(std::string appId) { if (!DString::endWith(appId, ".desktop")) appId += ".desktop"; for (const auto & dir : BaseDir::appDirs()) { std::string filePath = dir + appId; //检测文件有效性 if (DFile::isExisted(filePath)) { return DesktopInfo(filePath); } } return DesktopInfo(""); } bool DesktopInfo::getTerminal() { return m_keyFile.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); } // 按$PATH路径查找执行文件 bool DesktopInfo::findExecutable(std::string &exec) { static const char *path = getenv("PATH"); static std::vector paths = DString::splitChars(path, ':'); return std::any_of(paths.begin(), paths.end(), [&exec](std::string path) {return DFile::isExisted(path + "/" +exec);}); } /** * @brief DesktopInfo::getId * filename must has suffix desktopExt * example: * /usr/share/applications/a.desktop -> a * /usr/share/applications/kde4/a.desktop -> kde4/a * /xxxx/dir/a.desktop -> /xxxx/dir/a * @return */ std::string DesktopInfo::getId() { if (!m_id.empty()) return m_id; std::string idStr; auto const suffixPos = m_fileName.find(".desktop"); if (suffixPos == std::string::npos) return ""; idStr = m_fileName.substr(0, m_fileName.size() - 8); // trim suffix size_t dirPos = idStr.find("/applications/"); if (dirPos == std::string::npos) return idStr; std::string baseDir(idStr.substr(0, dirPos + 14)); // length of "/applications/" is 14 std::vector appDirs = BaseDir::appDirs(); bool installed = std::any_of(appDirs.begin(), appDirs.end(), [&baseDir](const auto &dir) {return dir == baseDir;}); if (installed) { m_id = idStr.substr(baseDir.size(), idStr.size()); } return m_id; } std::string DesktopInfo::getGenericName() { return m_keyFile.getLocaleStr(MainSection, KeyGenericName, ""); } std::string DesktopInfo::getName() { return m_name; } std::string DesktopInfo::getIcon() { return m_icon; } std::string DesktopInfo::getCommandLine() { return m_keyFile.getStr(MainSection, KeyExec); } std::vector DesktopInfo::getKeywords() { return m_keyFile.getLocaleStrList(MainSection, KeyKeywords, ""); } std::vector DesktopInfo::getCategories() { return m_keyFile.getStrList(MainSection, KeyCategories); } void DesktopInfo::setDesktopOverrideExec(const std::string &execStr) { m_overRideExec = execStr; } KeyFile *DesktopInfo::getKeyFile() { return &m_keyFile; } // class AppsDir AppsDir::AppsDir(const std::string &dirPath) : m_path(dirPath) { } AppsDir::~AppsDir() { } std::string AppsDir::getPath() { return m_path; } // 获取目录对应的应用名称 std::map AppsDir::getAppNames() { DIR* dp; struct dirent* ep; dp = opendir(m_path.c_str()); if (!dp) { std::cout << "Couldn't open directory " << m_path << std::endl; return m_appNames; } while ((ep = readdir(dp))) { if (ep->d_type != DT_REG && ep->d_type != DT_LNK) continue; if (!DString::endWith(ep->d_name, ".desktop")) continue; m_appNames.insert({ep->d_name, true}); } closedir(dp); return m_appNames; } // 获取所有应用信息 std::vector AppsDir::getAllDesktopInfos() { std::map recoder; std::vector desktopInfos; for (auto dir : BaseDir::appDirs()) { AppsDir appsDir(dir); std::map appNames = appsDir.getAppNames(); if (appNames.size() == 0) continue; for (const auto &iter : appNames) { if (recoder.find(iter.first) != recoder.end()) continue; std::string filePath = dir + iter.first; DesktopInfo desktopInfo(filePath); if (!desktopInfo.isValidDesktop()) continue; if (!desktopInfo.shouldShow()) continue; desktopInfos.push_back(std::move(desktopInfo)); recoder[iter.first] = true; } } return desktopInfos; }