--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/FileMonitor.cpp Sat Dec 02 15:02:56 2023 +0100
@@ -0,0 +1,127 @@
+/**
+ * ShaderShark
+ * Copyright © 2023 František Kučera (Frantovo.cz, GlobalCode.info)
+ *
+ * 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, version 3 of the License.
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <string>
+#include <stdexcept>
+#include <map>
+#include <sys/inotify.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "FileMonitor.h"
+
+void checkError(bool hasError, const std::string& message) {
+ if (hasError) throw std::logic_error(message + strerror(errno));
+}
+
+class WatchedFile::Impl {
+public:
+ int fd;
+ int wd;
+ std::string fileName;
+ uint32_t mask;
+ std::shared_ptr<FileMonitor::Impl> fileMonitorImpl;
+
+ virtual ~Impl();
+};
+
+WatchedFile::WatchedFile() {
+ impl = std::make_shared<Impl>();
+}
+
+WatchedFile::~WatchedFile() {
+}
+
+class FileMonitor::Impl {
+public:
+ int fd;
+ std::map<int, std::string> fileNames;
+
+ void unwatch(int wd) {
+ int result = inotify_rm_watch(fd, wd);
+ checkError(result < 0, "unable to stop monitoring a file: ");
+ fileNames.erase(wd);
+ }
+
+ virtual ~Impl() {
+ close(fd);
+ }
+
+};
+
+FileMonitor::FileMonitor() : FileMonitor(std::make_shared<Impl>()) {
+}
+
+FileMonitor::FileMonitor(std::shared_ptr<Impl> impl) {
+ this->impl = impl;
+ impl->fd = inotify_init1(IN_NONBLOCK);
+ checkError(impl->fd < 0, "unable to initialize FileMonitor: ");
+}
+
+FileMonitor::~FileMonitor() {
+}
+
+int FileMonitor::getFD() const {
+ return impl->fd;
+}
+
+WatchedFile FileMonitor::watch(const std::string& fileName) {
+ return watch(fileName, IN_CLOSE_WRITE);
+}
+
+WatchedFile FileMonitor::watch(const std::string& fileName, uint32_t mask) {
+ int wd = inotify_add_watch(impl->fd, fileName.c_str(), mask);
+ checkError(wd < 0, "unable to monitor a file: ");
+ impl->fileNames[wd] = fileName;
+ WatchedFile wf;
+ wf.impl->fd = impl->fd;
+ wf.impl->wd = wd;
+ wf.impl->fileMonitorImpl = impl;
+ wf.impl->fileName = fileName;
+ wf.impl->mask = mask;
+ return wf;
+}
+
+bool FileMonitor::readEvent(FileEvent& event) {
+ struct inotify_event rawEvent;
+ int result = ::read(impl->fd, &rawEvent, sizeof (rawEvent));
+ if (result == sizeof (rawEvent)) {
+ event.fileName = impl->fileNames[rawEvent.wd];
+ event.mask = rawEvent.mask;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+WatchedFile::Impl::~Impl() {
+ fileMonitorImpl->unwatch(wd);
+}
+
+FileMonitor WatchedFile::getFileMonitor() const {
+ return FileMonitor(impl->fileMonitorImpl);
+}
+
+std::string WatchedFile::getFileName() const {
+ return impl->fileName;
+}
+
+uint32_t WatchedFile::getMask() const {
+ return impl->mask;
+}
+
+