From 834cfad50233ded3439a2b7f8f6d293318b35196 Mon Sep 17 00:00:00 2001 From: Ted John Date: Fri, 30 Mar 2018 20:57:05 +0100 Subject: [PATCH] Create new FileWatcher class --- src/openrct2/core/FileWatcher.cpp | 102 ++++++++++++++++++++++++++++++ src/openrct2/core/FileWatcher.h | 33 ++++++++++ 2 files changed, 135 insertions(+) create mode 100644 src/openrct2/core/FileWatcher.cpp create mode 100644 src/openrct2/core/FileWatcher.h diff --git a/src/openrct2/core/FileWatcher.cpp b/src/openrct2/core/FileWatcher.cpp new file mode 100644 index 0000000000..7429ea8c97 --- /dev/null +++ b/src/openrct2/core/FileWatcher.cpp @@ -0,0 +1,102 @@ +#include +#include + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#include +#else +#include +#include +#include +#include +#endif + +#include "../core/String.hpp" +#include "FileWatcher.h" + +FileWatcher::FileWatcher(const std::string &directoryPath) +{ +#ifdef _WIN32 + _directoryHandle = CreateFileA(directoryPath.c_str(), FILE_LIST_DIRECTORY, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr); + if (_directoryHandle == INVALID_HANDLE_VALUE) + { + throw std::runtime_error("Unable to open directory '" + directoryPath + "'"); + } +#else + auto fd = inotify_init(); + if (fd >= 0) + { + auto wd = inotify_add_watch(fd, directoryPath.c_str(), IN_CLOSE_WRITE); + if (wd >= 0) + { + _fileDesc = fd; + _watchDesc = wd; + } + else + { + close(fd); + throw std::runtime_error("Unable to watch directory '" + directoryPath + "'"); + } + } +#endif + _watchThread = std::thread(std::bind(&FileWatcher::WatchDirectory, this)); +} + +FileWatcher::~FileWatcher() +{ +#ifdef _WIN32 + CancelIoEx(_directoryHandle, nullptr); + CloseHandle(_directoryHandle); +#else + inotify_rm_watch(_fileDesc, _watchDesc); + close(_fileDesc); +#endif + _watchThread.join(); +} + +void FileWatcher::WatchDirectory() +{ +#ifdef _WIN32 + std::array eventData; + DWORD bytesReturned; + while (ReadDirectoryChangesW(_directoryHandle, eventData.data(), (DWORD)eventData.size(), TRUE, FILE_NOTIFY_CHANGE_LAST_WRITE, &bytesReturned, nullptr, nullptr)) + { + auto onFileChanged = OnFileChanged; + if (onFileChanged) + { + FILE_NOTIFY_INFORMATION * notifyInfo; + size_t offset = 0; + do + { + notifyInfo = (FILE_NOTIFY_INFORMATION*)(eventData.data() + offset); + offset += notifyInfo->NextEntryOffset; + + std::wstring fileNameW(notifyInfo->FileName, notifyInfo->FileNameLength / sizeof(wchar_t)); + auto fileName = String::ToUtf8(fileNameW); + onFileChanged(fileName); + } + while (notifyInfo->NextEntryOffset != 0); + } + } +#else + std::array eventData; + auto length = read(_fileDesc, eventData.data(), eventData.size()); + if (length >= 0) + { + auto onFileChanged = OnFileChanged; + if (onFileChanged) + { + int offset = 0; + while (offset < length) + { + auto e = (inotify_event*)(eventData.data() + offset); + if ((e->mask & IN_CLOSE_WRITE) && !(e->mask & IN_ISDIR)) + { + onFileChanged(e->name); + } + offset += sizeof(inotify_event) + e->len; + } + } + } +#endif +} diff --git a/src/openrct2/core/FileWatcher.h b/src/openrct2/core/FileWatcher.h new file mode 100644 index 0000000000..9f923e9e15 --- /dev/null +++ b/src/openrct2/core/FileWatcher.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include + +#ifdef _WIN32 +typedef void * HANDLE; +#endif + +/** + * Creates a new thread that watches a directory tree for file modifications. + */ +class FileWatcher +{ +private: + std::thread _watchThread; +#ifdef _WIN32 + HANDLE _directoryHandle{}; +#else + int _fileDesc{}; + int _watchDesc{}; +#endif + +public: + std::function OnFileChanged; + + FileWatcher(const std::string &directoryPath); + ~FileWatcher(); + +private: + void WatchDirectory(); +};