From 960a989d05351c7d0c8bb3329a88d38cc91d1fa1 Mon Sep 17 00:00:00 2001 From: Umar Ahmed Date: Sun, 8 Jan 2023 08:18:13 -0500 Subject: [PATCH] Close #11473: Implement FileWatcher for macOS --- cmake/platform.cmake | 2 +- contributors.md | 1 + src/openrct2/core/FileScanner.cpp | 8 ++-- src/openrct2/core/FileScanner.h | 15 ++++--- src/openrct2/core/FileWatcher.cpp | 69 +++++++++++++++++++++++++++++++ src/openrct2/core/FileWatcher.h | 9 ++++ src/openrct2/entity/Guest.cpp | 6 +-- 7 files changed, 96 insertions(+), 14 deletions(-) diff --git a/cmake/platform.cmake b/cmake/platform.cmake index b7274fa827..d216e66e3c 100644 --- a/cmake/platform.cmake +++ b/cmake/platform.cmake @@ -3,7 +3,7 @@ function(target_link_platform_libraries target) if (APPLE) - target_link_libraries(${target} "-framework Cocoa") + target_link_libraries(${target} "-framework Cocoa -framework CoreServices") elseif(WIN32) target_link_libraries(${target} gdi32) endif () diff --git a/contributors.md b/contributors.md index 3939b85b09..73b218c67b 100644 --- a/contributors.md +++ b/contributors.md @@ -101,6 +101,7 @@ The following people are not part of the development team, but have been contrib * Andrew Pratt (andrewpratt64) - Added api hook for vehicle crashes, api function to get entities on a tile * Karst van Galen Last (AuraSpecs) - Ride paint (bounding boxes, extra track pieces), soundtrack, sound effects, misc. * (8street) - Misc. +* Umar Ahmed (umar-ahmed) - MacOS file watcher ## Bug fixes * (KirilAngelov) diff --git a/src/openrct2/core/FileScanner.cpp b/src/openrct2/core/FileScanner.cpp index 37a6175c85..2394cad7a1 100644 --- a/src/openrct2/core/FileScanner.cpp +++ b/src/openrct2/core/FileScanner.cpp @@ -73,7 +73,7 @@ private: std::stack _directoryStack; // Current - FileInfo* _currentFileInfo; + FileScanner::FileInfo* _currentFileInfo; utf8* _currentPath; public: @@ -84,7 +84,7 @@ public: _patterns = GetPatterns(Path::GetFileName(pattern)); _currentPath = Memory::Allocate(MAX_PATH); - _currentFileInfo = Memory::Allocate(); + _currentFileInfo = Memory::Allocate(); Reset(); } @@ -95,7 +95,7 @@ public: Memory::Free(_currentFileInfo); } - const FileInfo* GetFileInfo() const override + const FileScanner::FileInfo* GetFileInfo() const override { return _currentFileInfo; } @@ -349,7 +349,7 @@ void Path::QueryDirectory(QueryDirectoryResult* result, const std::string& patte auto scanner = Path::ScanDirectory(pattern, true); while (scanner->Next()) { - const FileInfo* fileInfo = scanner->GetFileInfo(); + const FileScanner::FileInfo* fileInfo = scanner->GetFileInfo(); const utf8* path = scanner->GetPath(); result->TotalFiles++; diff --git a/src/openrct2/core/FileScanner.h b/src/openrct2/core/FileScanner.h index 17081d0a67..da319d3fbd 100644 --- a/src/openrct2/core/FileScanner.h +++ b/src/openrct2/core/FileScanner.h @@ -16,18 +16,21 @@ #include #include -struct FileInfo +namespace FileScanner { - const utf8* Name; - uint64_t Size; - uint64_t LastModified; -}; + struct FileInfo + { + const utf8* Name; + uint64_t Size; + uint64_t LastModified; + }; +} // namespace FileScanner struct IFileScanner { virtual ~IFileScanner() = default; - virtual const FileInfo* GetFileInfo() const abstract; + virtual const FileScanner::FileInfo* GetFileInfo() const abstract; virtual const utf8* GetPath() const abstract; virtual const utf8* GetPathRelative() const abstract; diff --git a/src/openrct2/core/FileWatcher.cpp b/src/openrct2/core/FileWatcher.cpp index 43ffad70d1..d2292c9ec9 100644 --- a/src/openrct2/core/FileWatcher.cpp +++ b/src/openrct2/core/FileWatcher.cpp @@ -18,8 +18,11 @@ # include # include # include +#elif defined(__APPLE__) +# include #endif +#include "../core/Guard.hpp" #include "../core/Path.hpp" #include "../core/String.hpp" #include "FileSystem.hpp" @@ -82,6 +85,39 @@ FileWatcher::WatchDescriptor::~WatchDescriptor() } #endif +#if defined(__APPLE__) + +static int eventModified = kFSEventStreamEventFlagItemFinderInfoMod | kFSEventStreamEventFlagItemModified + | kFSEventStreamEventFlagItemInodeMetaMod | kFSEventStreamEventFlagItemChangeOwner | kFSEventStreamEventFlagItemXattrMod; + +static int eventRenamed = kFSEventStreamEventFlagItemCreated | kFSEventStreamEventFlagItemRemoved + | kFSEventStreamEventFlagItemRenamed; + +void FileWatcher::FSEventsCallback( + ConstFSEventStreamRef streamRef, void* clientCallBackInfo, size_t numEvents, void* eventPaths, + const FSEventStreamEventFlags eventFlags[], const FSEventStreamEventId eventIds[]) +{ + FileWatcher* fileWatcher = static_cast(clientCallBackInfo); + + Guard::Assert(fileWatcher != nullptr, "FileWatcher is null"); + Guard::Assert(fileWatcher->OnFileChanged != nullptr, "OnFileChanged is null"); + + char** paths = static_cast(eventPaths); + for (size_t i = 0; i < numEvents; i++) + { + if (eventFlags[i] & eventModified) + log_verbose("Modified: %s\n", paths[i]); + if (eventFlags[i] & eventRenamed) + log_verbose("Renamed: %s\n", paths[i]); + + if (eventFlags[i] & eventModified || eventFlags[i] & eventRenamed) + { + fileWatcher->OnFileChanged(paths[i]); + } + } +} +#endif + FileWatcher::FileWatcher(u8string_view directoryPath) { #ifdef _WIN32 @@ -103,6 +139,22 @@ FileWatcher::FileWatcher(u8string_view directoryPath) _watchDescs.emplace_back(_fileDesc.Fd, p.path().string()); } } +#elif defined(__APPLE__) + CFStringRef path = CFStringCreateWithCString(kCFAllocatorDefault, directoryPath.data(), kCFStringEncodingUTF8); + CFArrayRef pathsToWatch = CFArrayCreate(kCFAllocatorDefault, reinterpret_cast(&path), 1, nullptr); + CFAbsoluteTime latencyInSeconds = 0.5; + FSEventStreamContext context = { 0, this, nullptr, nullptr, nullptr }; + + _stream = FSEventStreamCreate( + NULL, &FSEventsCallback, &context, pathsToWatch, kFSEventStreamEventIdSinceNow, latencyInSeconds, + kFSEventStreamCreateFlagFileEvents); + + if (!_stream) + { + throw std::runtime_error("Unable to create FSEventStream"); + } + + _runLoop = CFRunLoopGetCurrent(); #else throw std::runtime_error("FileWatcher not supported on this platform."); #endif @@ -119,6 +171,16 @@ FileWatcher::~FileWatcher() _finished = true; _watchThread.join(); _fileDesc.Close(); +#elif defined(__APPLE__) + if (_stream) + { + FSEventStreamStop(_stream); + FSEventStreamUnscheduleFromRunLoop(_stream, _runLoop, kCFRunLoopDefaultMode); + FSEventStreamInvalidate(_stream); + FSEventStreamRelease(_stream); + } + _watchThread.join(); + _stream = nullptr; #else return; #endif @@ -186,5 +248,12 @@ void FileWatcher::WatchDirectory() // Sleep for 1/2 second usleep(500000); } +#elif defined(__APPLE__) + if (_stream) + { + FSEventStreamScheduleWithRunLoop(_stream, _runLoop, kCFRunLoopDefaultMode); + FSEventStreamStart(_stream); + CFRunLoopRun(); + } #endif } diff --git a/src/openrct2/core/FileWatcher.h b/src/openrct2/core/FileWatcher.h index 6670b59212..bac6617866 100644 --- a/src/openrct2/core/FileWatcher.h +++ b/src/openrct2/core/FileWatcher.h @@ -20,6 +20,8 @@ # include "FileSystem.hpp" typedef void* HANDLE; +#elif defined(__APPLE__) +# include #endif /** @@ -54,6 +56,9 @@ private: FileDescriptor _fileDesc; std::vector _watchDescs; +#elif defined(__APPLE__) + FSEventStreamRef _stream{}; + CFRunLoopRef _runLoop{}; #endif public: @@ -65,6 +70,10 @@ public: private: #if defined(_WIN32) || defined(__linux__) bool _finished{}; +#elif defined(__APPLE__) + static void FSEventsCallback( + ConstFSEventStreamRef streamRef, void* clientCallBackInfo, size_t numEvents, void* eventPaths, + const FSEventStreamEventFlags eventFlags[], const FSEventStreamEventId eventIds[]); #endif void WatchDirectory(); diff --git a/src/openrct2/entity/Guest.cpp b/src/openrct2/entity/Guest.cpp index 676dee5bc6..1f06f20d22 100644 --- a/src/openrct2/entity/Guest.cpp +++ b/src/openrct2/entity/Guest.cpp @@ -1851,9 +1851,9 @@ Ride* Guest::FindBestRideToGoOn() return mostExcitingRide; } -BitSet Guest::FindRidesToGoOn() +OpenRCT2::BitSet Guest::FindRidesToGoOn() { - BitSet rideConsideration; + OpenRCT2::BitSet rideConsideration; // FIX Originally checked for a toy, likely a mistake and should be a map, // but then again this seems to only allow the peep to go on @@ -3134,7 +3134,7 @@ template static void peep_head_for_nearest_ride(Guest* peep, bool co } } - BitSet rideConsideration; + OpenRCT2::BitSet rideConsideration; if (!considerOnlyCloseRides && (peep->HasItem(ShopItem::Map))) { // Consider all rides in the park