9#define WIN32_LEAN_AND_MEAN
12#include <brenta/fswatcher.hpp>
17struct FsWatcherWindowsDirList
20 WCHAR pathW[MAX_PATH];
22 BYTE buffer[1024 * 64];
24 OVERLAPPED overlapped;
25 FsWatcherWindowsDirList *next;
28struct FsWatcherWindows
31 FsWatcherWindowsDirList *dir_list;
36using namespace brenta;
38FilesystemWatcher::~FilesystemWatcher()
43bool FilesystemWatcher::init()
45 FsWatcherWindows *fw_windows =
new FsWatcherWindows();
47 fw_windows->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
51 if (!fw_windows->iocp)
57 this->internal =
static_cast<void*
>(fw_windows);
61void FilesystemWatcher::destroy()
63 if (!this->internal)
return;
65 FsWatcherWindows *fw_windows =
static_cast<FsWatcherWindows*
>(this->internal);
67 FsWatcherWindowsDirList *it = fw_windows->dir_list;
68 FsWatcherWindowsDirList *next = NULL;
72 CloseHandle(it->hDir);
74 PostQueuedCompletionStatus(fw_windows->iocp, 0, (ULONG_PTR)NULL, NULL);
78 CloseHandle(fw_windows->iocp);
80 delete static_cast<FsWatcherWindows*
>(this->internal);
81 this->internal =
nullptr;
84bool FilesystemWatcher::add(
const std::filesystem::path &path,
85 tenno::vector<Event> events)
87 if (!this->internal)
return false;
89 FsWatcherWindows *fw_windows =
static_cast<FsWatcherWindows*
>(this->internal);
92 for (
auto& event : events)
96 case FilesystemWatcher::Event::Modify:
97 filter |= FILE_NOTIFY_CHANGE_LAST_WRITE;
105 int len = MultiByteToWideChar(CP_UTF8, 0, path.string().c_str(), -1, NULL, 0);
106 WCHAR pathW[MAX_PATH];
107 MultiByteToWideChar(CP_UTF8, 0, path.string().c_str(), -1, pathW, len);
109 wchar_t full_path[MAX_PATH];
110 wchar_t *file_part = NULL;
112 if (GetFullPathNameW(pathW,
118 FsWatcherWindowsDirList *fw_dir =
new FsWatcherWindowsDirList();
119 fw_dir->filter = filter;
120 strcpy(fw_dir->path, path.string().c_str());
122 if (file_part != NULL)
124 wcscpy(fw_dir->pathW, file_part);
128 HANDLE hDir = CreateFileW(full_path,
130 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
133 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
135 if (hDir == INVALID_HANDLE_VALUE)
144 if (CreateIoCompletionPort(fw_dir->hDir,
154 if (ReadDirectoryChangesW(fw_dir->hDir,
156 sizeof(fw_dir->buffer),
160 &fw_dir->overlapped, NULL) == 0)
167 FsWatcherWindowsDirList *it = fw_windows->dir_list;
170 fw_windows->dir_list = fw_dir;
173 while(it->next) { it = it->next; }
178bool FilesystemWatcher::add(
const tenno::vector<std::filesystem::path> &paths,
179 tenno::vector<Event> events)
182 for (
auto& path : paths)
184 out |= this->add(path, events);
189bool FilesystemWatcher::rm(
const std::filesystem::path &path)
191 if (!this->internal)
return false;
192 FsWatcherWindows *fw_windows =
static_cast<FsWatcherWindows*
>(this->internal);
193 FsWatcherWindowsDirList **curr = &fw_windows->dir_list;
197 if ((*curr)->path == path)
199 FsWatcherWindowsDirList *to_remove = *curr;
201 *curr = to_remove->next;
203 CancelIoEx(to_remove->hDir, &to_remove->overlapped);
204 CloseHandle(to_remove->hDir);
208 curr = &((*curr)->next);
214bool FilesystemWatcher::rm(
const tenno::vector<std::filesystem::path> &paths)
217 for (
auto& path : paths)
219 out |= this->rm(path);
224std::optional<std::filesystem::path> FilesystemWatcher::watch()
226 if (!this->internal)
return {};
227 FsWatcherWindows *fw_windows =
static_cast<FsWatcherWindows*
>(this->internal);
231 LPOVERLAPPED overlapped;
233 while(GetQueuedCompletionStatus(fw_windows->iocp,
239 if (key == 0)
return {};
241 FsWatcherWindowsDirList* fw_dir = (FsWatcherWindowsDirList*)key;
249 BYTE* pRaw = fw_dir->buffer;
250 FILE_NOTIFY_INFORMATION* info = NULL;
254 info = (FILE_NOTIFY_INFORMATION*)pRaw;
256 if (wcsncmp(info->FileName,
258 info->FileNameLength /
sizeof(WCHAR)) == 0)
260 ReadDirectoryChangesW(fw_dir->hDir,
262 sizeof(fw_dir->buffer),
272 pRaw += info->NextEntryOffset;
274 }
while (info->NextEntryOffset != 0);
276 ReadDirectoryChangesW(fw_dir->hDir,
278 sizeof(fw_dir->buffer),
280 fw_dir->filter, NULL,