Skip to content

Commit 2ab1364

Browse files
finders/desktop: various path handling fixes (#37)
get file locations from environment, check subdirectories and do not cache duplicate desktop file ids * finders/desktop: follow XDG Base Dir spec for getting desktop entry paths * finders/desktop: recursively cache desktop files from subdirectories and only cache first instance of every desktop file id * desktop: skipping directories already visited, no longer possible to infinitely recurse
1 parent e629f1a commit 2ab1364

2 files changed

Lines changed: 57 additions & 45 deletions

File tree

src/finders/desktop/DesktopFinder.cpp

Lines changed: 47 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <fstream>
1010
#include <sys/inotify.h>
1111
#include <sys/poll.h>
12+
#include <unordered_set>
1213

1314
#include <hyprutils/string/String.hpp>
1415
#include <hyprutils/os/Process.hpp>
@@ -17,13 +18,13 @@
1718
using namespace Hyprutils::String;
1819
using namespace Hyprutils::OS;
1920

20-
static std::optional<std::string> readFileAsString(const std::string& path) {
21+
static std::optional<std::string> readFileAsString(const std::filesystem::path& path) {
2122
std::error_code ec;
2223

2324
if (!std::filesystem::exists(path, ec) || ec)
2425
return std::nullopt;
2526

26-
std::ifstream file(path);
27+
std::ifstream file(path.string());
2728
if (!file.good())
2829
return std::nullopt;
2930

@@ -96,25 +97,19 @@ static std::filesystem::path resolvePath(const std::string& p) {
9697
return std::filesystem::path(HOME) / p.substr(2);
9798
}
9899

99-
static const std::array<std::filesystem::path, 3> DESKTOP_ENTRY_PATHS = {"/usr/local/share/applications", "/usr/share/applications", resolvePath("~/.local/share/applications")};
100-
101100
CDesktopFinder::CDesktopFinder() : m_inotifyFd(inotify_init()), m_entryFrequencyCache(makeUnique<CEntryCache>("desktop")) {
102-
const auto ENV = getenv("XDG_DATA_DIRS");
103-
if (!ENV)
104-
return;
105-
106-
CConstVarList paths(ENV, 0, ':', false);
107-
108-
for (const auto& p : paths) {
109-
const std::filesystem::path PTH = std::filesystem::path(p) / "applications";
110-
std::error_code ec;
111-
if (!std::filesystem::exists(PTH, ec) || ec)
112-
continue;
113-
114-
if (std::ranges::contains(DESKTOP_ENTRY_PATHS, PTH))
115-
continue;
116-
117-
m_envPaths.emplace_back(PTH);
101+
if (const auto DATA_HOME = getenv("XDG_DATA_HOME"))
102+
m_envPaths.emplace_back(std::filesystem::path(DATA_HOME) / "applications");
103+
else
104+
m_envPaths.emplace_back(resolvePath("~/.local/share/applications"));
105+
106+
if (const auto DATA_DIRS = getenv("XDG_DATA_DIRS")) {
107+
CConstVarList paths(DATA_DIRS, 0, ':', false);
108+
for (const auto& p : paths)
109+
m_envPaths.emplace_back(std::filesystem::path(p) / "applications");
110+
} else {
111+
m_envPaths.emplace_back("/usr/local/share/applications");
112+
m_envPaths.emplace_back("/usr/share/applications");
118113
}
119114
}
120115

@@ -134,26 +129,42 @@ void CDesktopFinder::recache() {
134129
m_desktopEntryCache.clear();
135130
m_desktopEntryCacheGeneric.clear();
136131

137-
auto cachePath = [this](const std::string& p) {
132+
std::unordered_set<std::string> desktopFileIds;
133+
std::unordered_set<std::filesystem::path> directories;
134+
135+
std::function<void (const std::filesystem::path&, const std::filesystem::path&)> cacheDirectory;
136+
cacheDirectory = [this, &cacheDirectory, &desktopFileIds, &directories](const std::filesystem::path& base, const std::filesystem::path& p) {
138137
std::error_code ec;
139-
auto it = std::filesystem::directory_iterator(resolvePath(p), ec);
140-
if (ec)
138+
auto canonicalPath = std::filesystem::canonical(p, ec);
139+
if (ec || !directories.insert(canonicalPath).second) {
140+
Debug::log(TRACE, "desktop: skipping {}, does not exist / already visited", p.string());
141141
return;
142+
}
143+
auto it = std::filesystem::directory_iterator(p, ec);
144+
if (ec) return;
142145
for (const auto& e : it) {
143-
if (!e.is_regular_file(ec) || ec)
144-
continue;
145-
146-
cacheEntry(e.path().string());
146+
auto status = e.status(ec);
147+
if (ec) continue;
148+
if (std::filesystem::is_regular_file(status)) {
149+
auto relDesktopFilePath = e.path().lexically_relative(base);
150+
if (relDesktopFilePath.extension() != ".desktop") {
151+
Debug::log(TRACE, "desktop: skipping non-desktop file at {}", e.path().string());
152+
continue;
153+
}
154+
auto desktopFileId = relDesktopFilePath.string();
155+
std::ranges::replace(desktopFileId, '/', '-');
156+
if (desktopFileIds.insert(desktopFileId).second)
157+
cacheEntry(e.path());
158+
else Debug::log(TRACE, "desktop: skipping entry at {}, already cached desktopFileId {}", e.path().string(), desktopFileId);
159+
} else if (std::filesystem::is_directory(status))
160+
cacheDirectory(base, e.path());
147161
}
148162

149-
m_desktopEntryPaths.emplace_back(resolvePath(p));
163+
m_desktopEntryPaths.emplace_back(p);
150164
};
151165

152-
for (const auto& PATH : DESKTOP_ENTRY_PATHS) {
153-
cachePath(PATH);
154-
}
155166
for (const auto& PATH : m_envPaths) {
156-
cachePath(PATH);
167+
cacheDirectory(PATH, PATH);
157168
}
158169
}
159170

@@ -185,8 +196,8 @@ void CDesktopFinder::replantWatch() {
185196
}
186197
}
187198

188-
void CDesktopFinder::cacheEntry(const std::string& path) {
189-
Debug::log(TRACE, "desktop: caching entry {}", path);
199+
void CDesktopFinder::cacheEntry(const std::filesystem::path& path) {
200+
Debug::log(TRACE, "desktop: caching entry at {}", path.string());
190201

191202
const auto READ_RESULT = readFileAsString(path);
192203

@@ -228,7 +239,7 @@ void CDesktopFinder::cacheEntry(const std::string& path) {
228239
const auto NODISPLAY = extract("NoDisplay") == "true";
229240

230241
if (EXEC.empty() || NAME.empty() || NODISPLAY) {
231-
Debug::log(TRACE, "Skipping entry, empty name / exec / NoDisplay");
242+
Debug::log(TRACE, "desktop: skipping entry, empty name / exec / NoDisplay");
232243
return;
233244
}
234245

@@ -241,7 +252,7 @@ void CDesktopFinder::cacheEntry(const std::string& path) {
241252
e->m_frequency = m_entryFrequencyCache->getCachedEntry(e->m_fuzzable);
242253
m_desktopEntryCacheGeneric.emplace_back(e);
243254

244-
Debug::log(TRACE, "Cached: {} with icon {} and exec line of \"{}\"", NAME, ICON, EXEC);
255+
Debug::log(TRACE, "desktop: cached {} with icon {} and exec line of \"{}\"", NAME, ICON, EXEC);
245256
}
246257

247258
std::vector<SFinderResult> CDesktopFinder::getResultsForQuery(const std::string& query) {

src/finders/desktop/DesktopFinder.hpp

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "../IFinder.hpp"
44

55
#include <hyprutils/os/FileDescriptor.hpp>
6+
#include <filesystem>
67

78
class CDesktopEntry;
89
class CEntryCache;
@@ -20,19 +21,19 @@ class CDesktopFinder : public IFinder {
2021
void onInotifyEvent();
2122

2223
private:
23-
std::vector<SP<CDesktopEntry>> m_desktopEntryCache;
24-
std::vector<SP<IFinderResult>> m_desktopEntryCacheGeneric;
24+
std::vector<SP<CDesktopEntry>> m_desktopEntryCache;
25+
std::vector<SP<IFinderResult>> m_desktopEntryCacheGeneric;
2526

26-
std::vector<std::string> m_desktopEntryPaths;
27-
std::vector<int> m_watches;
27+
std::vector<std::filesystem::path> m_desktopEntryPaths;
28+
std::vector<int> m_watches;
2829

29-
std::vector<std::string> m_envPaths;
30+
std::vector<std::filesystem::path> m_envPaths;
3031

31-
UP<CEntryCache> m_entryFrequencyCache;
32+
UP<CEntryCache> m_entryFrequencyCache;
3233

33-
void cacheEntry(const std::string& path);
34-
void replantWatch();
35-
void recache();
34+
void cacheEntry(const std::filesystem::path& path);
35+
void replantWatch();
36+
void recache();
3637

3738
friend class CDesktopEntry;
3839
};

0 commit comments

Comments
 (0)