You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libcutefish/files/trashmonitor.cpp

112 lines
2.9 KiB
C++

// SPDX-FileCopyrightText: 2026 Reion Wong <reionwong@gmail.com>
// SPDX-License-Identifier: GPL-3.0-or-later
#include "files/trashmonitor.h"
#include <algorithm>
#include <chrono>
#include <dirent.h>
#include <sys/stat.h>
#include "trashdirectory_p.h"
namespace cutefish::files {
using namespace std::chrono_literals;
static constexpr std::chrono::milliseconds FirstRetry = 2000ms;
static constexpr std::chrono::milliseconds LastRetry = 60000ms;
static bool hasEntries(const std::string& path)
{
DIR* dir = ::opendir(path.c_str());
if (!dir)
return false;
bool found = false;
while (const dirent* entry = ::readdir(dir)) {
const std::string_view name = entry->d_name;
if (name != "." && name != "..") {
found = true;
break;
}
}
::closedir(dir);
return found;
}
TrashMonitor::TrashMonitor()
{
m_debounce.setSingleShot(true);
m_debounce.setInterval(40ms);
m_retry.setSingleShot(true);
m_retry.setInterval(FirstRetry);
m_watchChanged = m_watch.changed.connect([this] {
m_debounce.start();
});
m_debounce.onTimeout = [this] {
refresh();
};
m_retry.onTimeout = [this] {
refresh();
};
refresh();
}
void TrashMonitor::refresh()
{
if (m_job.isPending()) {
m_pending = true;
return;
}
// Looking for the trash folders reads the mount table and stats each mount.
m_job = base::ThreadPool::global().run(
[] {
State state;
Internal::trashWatches(&state.paths, &state.entries);
for (const auto& trash : Internal::trashDirectories()) {
if (hasEntries(trash.path + "/files")) {
state.empty = false;
break;
}
}
return state;
},
[this](State state) {
// Mounts and unmounts add and remove volumes' trash, so a folder found can be
// gone by now.
std::vector<std::string> paths;
for (const auto& path : state.paths) {
struct stat st;
if (::stat(path.c_str(), &st) == 0 && S_ISDIR(st.st_mode))
paths.push_back(path);
}
// A watch that keeps failing, past the inotify limit say, is tried less often.
if (m_watch.watch(paths, state.entries, true)) {
m_retry.stop();
m_retry.setInterval(FirstRetry);
} else {
m_retry.start();
m_retry.setInterval(std::min<std::chrono::nanoseconds>(m_retry.interval() * 2, LastRetry));
}
if (state.empty != m_empty) {
m_empty = state.empty;
emptyChanged.emit();
}
if (m_pending) {
m_pending = false;
refresh();
}
});
}
} // namespace cutefish::files