Close #11473: Implement FileWatcher for macOS

This commit is contained in:
Umar Ahmed 2023-01-08 08:18:13 -05:00 committed by GitHub
parent f5043183f7
commit 960a989d05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 96 additions and 14 deletions

View File

@ -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 ()

View File

@ -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)

View File

@ -73,7 +73,7 @@ private:
std::stack<DirectoryState> _directoryStack;
// Current
FileInfo* _currentFileInfo;
FileScanner::FileInfo* _currentFileInfo;
utf8* _currentPath;
public:
@ -84,7 +84,7 @@ public:
_patterns = GetPatterns(Path::GetFileName(pattern));
_currentPath = Memory::Allocate<utf8>(MAX_PATH);
_currentFileInfo = Memory::Allocate<FileInfo>();
_currentFileInfo = Memory::Allocate<FileScanner::FileInfo>();
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++;

View File

@ -16,18 +16,21 @@
#include <string>
#include <vector>
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;

View File

@ -18,8 +18,11 @@
# include <sys/inotify.h>
# include <sys/types.h>
# include <unistd.h>
#elif defined(__APPLE__)
# include <CoreServices/CoreServices.h>
#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<FileWatcher*>(clientCallBackInfo);
Guard::Assert(fileWatcher != nullptr, "FileWatcher is null");
Guard::Assert(fileWatcher->OnFileChanged != nullptr, "OnFileChanged is null");
char** paths = static_cast<char**>(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<const void**>(&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
}

View File

@ -20,6 +20,8 @@
# include "FileSystem.hpp"
typedef void* HANDLE;
#elif defined(__APPLE__)
# include <CoreServices/CoreServices.h>
#endif
/**
@ -54,6 +56,9 @@ private:
FileDescriptor _fileDesc;
std::vector<WatchDescriptor> _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();

View File

@ -1851,9 +1851,9 @@ Ride* Guest::FindBestRideToGoOn()
return mostExcitingRide;
}
BitSet<OpenRCT2::Limits::MaxRidesInPark> Guest::FindRidesToGoOn()
OpenRCT2::BitSet<OpenRCT2::Limits::MaxRidesInPark> Guest::FindRidesToGoOn()
{
BitSet<OpenRCT2::Limits::MaxRidesInPark> rideConsideration;
OpenRCT2::BitSet<OpenRCT2::Limits::MaxRidesInPark> 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<typename T> static void peep_head_for_nearest_ride(Guest* peep, bool co
}
}
BitSet<OpenRCT2::Limits::MaxRidesInPark> rideConsideration;
OpenRCT2::BitSet<OpenRCT2::Limits::MaxRidesInPark> rideConsideration;
if (!considerOnlyCloseRides && (peep->HasItem(ShopItem::Map)))
{
// Consider all rides in the park