diff src/core/filesystem.cc @ 382:0265e125f680

filesystem: implement filesystem watcher I also ported the library code to use it as well. Once we implement proper directory watching on Windows (and maybe others) this will be fairly useful :)
author Paper <paper@tflc.us>
date Thu, 06 Nov 2025 03:16:55 -0500
parents 5912dafc6e28
children
line wrap: on
line diff
--- a/src/core/filesystem.cc	Thu Nov 06 01:17:24 2025 -0500
+++ b/src/core/filesystem.cc	Thu Nov 06 03:16:55 2025 -0500
@@ -56,4 +56,118 @@
 	return GetDotPath() / "anime" / "posters";
 }
 
+/* ------------------------------------------------------------------------ */
+/* Ehhhhh */
+
+class Watcher : public IWatcher {
+public:
+	Watcher(void *opaque, const std::filesystem::path &path, EventHandler handler)
+		: path_(path)
+		, handler_(handler)
+		, opaque_(opaque)
+	{
+	}
+
+	virtual ~Watcher() override
+	{
+	}
+
+protected:
+	std::filesystem::path path_;
+	EventHandler handler_;
+	void *opaque_;
+};
+
+/* ------------------------------------------------------------------------ */
+/* Non-recursive filesystem watcher.
+ * This is the portable version for non-Windows */
+
+template<typename T>
+class StdFilesystemWatcher : public Watcher {
+public:
+	StdFilesystemWatcher(void *opaque, const std::filesystem::path &path, EventHandler handler)
+		: Watcher(opaque, path, handler)
+	{
+	}
+
+	virtual ~StdFilesystemWatcher() override
+	{
+	}
+
+	virtual void Process() override
+	{
+		/* Untoggle all paths. This allows us to only ever
+		 * iterate over the directory ONCE. */
+		UntoggleAllPaths();
+
+		for (const auto &item : T(path_)) {
+			std::filesystem::path p = item.path();
+
+			if (FindAndTogglePath(p))
+				continue;
+
+			/* Hand the path off to the listener */
+			handler_(opaque_, p, Event::Created);
+			paths_.push_back({true, p});
+		}
+
+		DeleteUntoggledPaths();
+	}
+
+protected:
+	bool FindAndTogglePath(const std::filesystem::path &p) {
+		for (auto &pp : paths_) {
+			if (pp.path == p) {
+				pp.found = true;
+				return true;
+			}
+		}
+
+		return false;
+	}
+
+	void UntoggleAllPaths()
+	{
+		for (auto &pp : paths_)
+			pp.found = false;
+	}
+
+	void DeleteUntoggledPaths()
+	{
+		for (const auto &path : paths_)
+			if (!path.found)
+				handler_(opaque_, path.path, Event::Deleted);
+
+		auto it = paths_.begin();
+
+		while (it != paths_.end()) {
+			if (!it->found) {
+				it = paths_.erase(it);
+			} else {
+				it++;
+			}
+		}
+	}
+
+	struct PathStatus {
+		bool found;
+		std::filesystem::path path;
+	};
+
+	/* TODO this is probably DAMN slow */
+	std::vector<PathStatus> paths_;
+};
+
+IWatcher *GetRecursiveFilesystemWatcher(void *opaque,
+	const std::filesystem::path &path, IWatcher::EventHandler handler)
+{
+	/* .... :) */
+	return new StdFilesystemWatcher<std::filesystem::recursive_directory_iterator>(opaque, path, handler);
+}
+
+IWatcher *GetFilesystemWatcher(void *opaque, const std::filesystem::path &path, IWatcher::EventHandler handler)
+{
+	return new StdFilesystemWatcher<std::filesystem::directory_iterator>(opaque, path, handler);
+}
+
 } // namespace Filesystem