2#include "common/config.h"
6#include <Poco/Delegate.h>
7#include <Poco/DirectoryWatcher.h>
20struct FileWatch::DirWatch {};
23#ifndef EVENGINE_WEBGPU
26std::string basenameOf(
const std::string &path) {
28 return p.getFileName();
31std::string normalizeDir(
const std::string &
dir) {
35 char *resolved = realpath(
dir.c_str(),
nullptr);
46 while (
s.size() > 1 && (
s.back() ==
'/' ||
s.back() ==
'\\'))
s.pop_back();
54 std::unique_ptr<Poco::DirectoryWatcher>
watcher;
55 std::unordered_map<std::string, std::string>
filters;
64bool FileWatch::add(
const std::string &realDir,
const std::string &filterName,
65 const std::string &reportPath,
int scanInterval) {
74 if (realDir.empty() || reportPath.empty())
return false;
75 const std::string
dir = normalizeDir(realDir);
78 if (!
f.exists() || !
f.isDirectory())
return false;
90 std::unique_ptr<DirWatch> doomed;
93 std::lock_guard<std::mutex> lock(mu_);
95 auto prev = reportToDir_.find(reportPath);
96 if (prev != reportToDir_.end()) {
97 auto it = byDir_.find(prev->second);
98 if (it != byDir_.end()) {
101 if (fit->second == reportPath) {
109 doomed = std::move(it->second);
113 reportToDir_.erase(prev);
117 auto it = byDir_.find(
dir);
118 if (it == byDir_.end()) {
119 auto owned = std::make_unique<DirWatch>();
120 owned->realDir =
dir;
122 owned->watcher = std::make_unique<Poco::DirectoryWatcher>(
123 dir, Poco::DirectoryWatcher::DW_FILTER_ENABLE_ALL, scanInterval);
127 if (owned->watcher) {
129 byDir_[
dir] = std::move(owned);
132 dw = it->second.get();
136 dw->
watcher->itemAdded += Poco::delegate(
this, &FileWatch::onAdded);
137 dw->
watcher->itemRemoved += Poco::delegate(
this, &FileWatch::onRemoved);
138 dw->
watcher->itemModified += Poco::delegate(
this, &FileWatch::onModified);
139 dw->
watcher->itemMovedFrom += Poco::delegate(
this, &FileWatch::onMovedFrom);
140 dw->
watcher->itemMovedTo += Poco::delegate(
this, &FileWatch::onMovedTo);
141 dw->
filters[filterName] = reportPath;
143 reportToDir_[reportPath] =
dir;
147 if (doomed) unsubscribeDelegates(doomed.get());
153#ifdef EVENGINE_WEBGPU
160 std::unique_ptr<DirWatch> doomed;
162 std::lock_guard<std::mutex> lock(mu_);
163 auto prev = reportToDir_.find(reportPath);
164 if (prev == reportToDir_.end())
return false;
165 const std::string
dir = prev->second;
166 reportToDir_.erase(prev);
168 auto it = byDir_.find(
dir);
169 if (it == byDir_.end())
return false;
172 if (fit->second == reportPath) {
180 doomed = std::move(it->second);
184 if (doomed) unsubscribeDelegates(doomed.get());
190#ifdef EVENGINE_WEBGPU
191 std::lock_guard<std::mutex> lock(mu_);
199 std::vector<std::unique_ptr<DirWatch>> doomed;
201 std::lock_guard<std::mutex> lock(mu_);
202 for (
auto &kv : byDir_) doomed.push_back(std::move(kv.second));
204 reportToDir_.clear();
207 for (
const auto &dw : doomed) unsubscribeDelegates(dw.get());
212 std::lock_guard<std::mutex> lock(mu_);
213#ifdef EVENGINE_WEBGPU
216 return int(reportToDir_.size());
221 std::lock_guard<std::mutex> lock(mu_);
222 if (queue_.empty())
return false;
223 out = std::move(queue_.front());
224 queue_.erase(queue_.begin());
228#ifndef EVENGINE_WEBGPU
229void FileWatch::unsubscribeDelegates(DirWatch *dw) {
230 if (!dw || !dw->watcher)
return;
231 dw->watcher->itemAdded -= Poco::delegate(
this, &FileWatch::onAdded);
232 dw->watcher->itemRemoved -= Poco::delegate(
this, &FileWatch::onRemoved);
233 dw->watcher->itemModified -= Poco::delegate(
this, &FileWatch::onModified);
234 dw->watcher->itemMovedFrom -= Poco::delegate(
this, &FileWatch::onMovedFrom);
235 dw->watcher->itemMovedTo -= Poco::delegate(
this, &FileWatch::onMovedTo);
238void FileWatch::handlePocoEvent(
const std::string &
kind,
const std::string &itemPath) {
239 const std::string
name = basenameOf(itemPath);
240 Poco::Path
parent(itemPath);
242 const std::string
dir = normalizeDir(
parent.toString());
244 std::lock_guard<std::mutex> lock(mu_);
245 auto it = byDir_.find(
dir);
246 if (it == byDir_.end()) {
247 for (
auto &kv : byDir_) {
248 if (itemPath.size() >= kv.first.size() && itemPath.compare(0, kv.first.size(), kv.first) == 0) {
249 it = byDir_.find(kv.first);
254 if (it == byDir_.end())
return;
255 DirWatch *dw = it->second.get();
257 auto push = [&](
const std::string &report) {
261 ev.realPath = itemPath;
262 queue_.push_back(std::move(ev));
265 auto all = dw->filters.find(
"");
266 if (all != dw->filters.end()) {
267 std::string report = all->second;
268 if (!report.empty() && report.back() !=
'/') report +=
"/";
273 auto fit = dw->filters.find(
name);
274 if (fit != dw->filters.end()) push(fit->second);
277void FileWatch::onAdded(
const void *,
const Poco::DirectoryWatcher::DirectoryEvent &event) {
278 handlePocoEvent(
"added", event.item.path());
281void FileWatch::onRemoved(
const void *,
const Poco::DirectoryWatcher::DirectoryEvent &event) {
282 handlePocoEvent(
"removed", event.item.path());
285void FileWatch::onModified(
const void *,
const Poco::DirectoryWatcher::DirectoryEvent &event) {
286 handlePocoEvent(
"modified", event.item.path());
289void FileWatch::onMovedFrom(
const void *,
const Poco::DirectoryWatcher::DirectoryEvent &event) {
290 handlePocoEvent(
"movedFrom", event.item.path());
293void FileWatch::onMovedTo(
const void *,
const Poco::DirectoryWatcher::DirectoryEvent &event) {
294 handlePocoEvent(
"movedTo", event.item.path());
bool add(const std::string &realDir, const std::string &filterName, const std::string &reportPath, int scanInterval=1)
bool poll(Event &out)
Pop one event; false if empty.
bool remove(const std::string &reportPath)
Remove by reportPath previously passed to add().
std::unique_ptr< Poco::DirectoryWatcher > watcher
std::unordered_map< std::string, std::string > filters