From e77054b80568f65aaf8a6201cf8432d000288209 Mon Sep 17 00:00:00 2001 From: ladevieq Date: Wed, 20 Jul 2022 00:09:03 +0200 Subject: Add file watching api --- src/utils.cpp | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 88 insertions(+), 2 deletions(-) (limited to 'src/utils.cpp') diff --git a/src/utils.cpp b/src/utils.cpp index dc5791c..9994064 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -45,6 +45,92 @@ std::vector read_file(const char* path) { return file_content; } -std::vector read_file(std::string& path) { - return read_file(path.c_str()); + +#ifdef WINDOWS +void log_last_error() { + auto error_code = GetLastError(); + const char* error_message = nullptr; + + FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, + error_code, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &error_message, + 0, + nullptr + ); + + std::cerr << error_message << std::endl; } + +bool watcher::watch_file(std::filesystem::path&& filepath, std::function callback) { + if (!std::filesystem::exists(filepath)) + return false; + + if (!watcher::watch_dir(filepath.parent_path())) + return false; + + auto [_, is_inserted] = watcher::callbacks.emplace(filepath.filename().string(), callback); + + return is_inserted; +} + +bool watcher::watch_dir(std::filesystem::path&& dir_path) { + if (!std::filesystem::exists(dir_path)) + return false; + + if (watcher::watched_dirs.find(dir_path.string()) != watcher::watched_dirs.end()) + return true; + + HANDLE dir_handle = CreateFileW( + dir_path.c_str(), + FILE_LIST_DIRECTORY, + FILE_SHARE_WRITE | FILE_SHARE_READ, + nullptr, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, + nullptr + ); + + if(dir_handle == INVALID_HANDLE_VALUE) { + log_last_error(); + return false; + } + + auto [dir, _] = watcher::watched_dirs.emplace(dir_path.string(), watch_data{ .dir_handle = dir_handle }); + + if (FAILED(ReadDirectoryChangesW(dir_handle, dir->second.buffer, sizeof(watch_data::buffer), FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE, nullptr, &dir->second.overlapped, nullptr))) { + log_last_error(); + return false; + } + + return true; +} + +void watcher::pull_changes() { + for (auto& dir : watcher::watched_dirs) { + auto& watch_data = dir.second; + if (!HasOverlappedIoCompleted(&watch_data.overlapped)) + continue; + + auto* dir_event = (PFILE_NOTIFY_INFORMATION)watch_data.buffer; + do { + if (dir_event->Action == FILE_ACTION_MODIFIED) { + auto w_filename = std::wstring{ dir_event->FileName, dir_event->FileNameLength / sizeof(wchar_t) }; + auto filename = std::string{w_filename.begin(), w_filename.end()}; + if (callbacks.contains(filename)) + callbacks[filename](); + } + + dir_event += dir_event->NextEntryOffset; + } while(dir_event->NextEntryOffset != 0); + + if (FAILED(ReadDirectoryChangesW(watch_data.dir_handle, watch_data.buffer, sizeof(watch_data::buffer), FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE, nullptr, &watch_data.overlapped, nullptr))) { + log_last_error(); + } + } +} +#endif -- cgit v1.3