// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. #if PLATFORM_MAC #include "MacFileSystemWatcher.h" #include "Engine/Platform/Apple/AppleUtils.h" #include "Engine/Platform/CriticalSection.h" #include "Engine/Platform/Thread.h" #include "Engine/Threading/ThreadSpawner.h" #include "Engine/Core/Collections/Array.h" #include "Engine/Core/Types/StringView.h" void DirectoryWatchCallback( ConstFSEventStreamRef StreamRef, void* FileWatcherPtr, size_t EventCount, void* EventPaths, const FSEventStreamEventFlags EventFlags[], const FSEventStreamEventId EventIDs[] ) { MacFileSystemWatcher* macFileSystemWatcher = (MacFileSystemWatcher*)FileWatcherPtr; if (macFileSystemWatcher) { CFArrayRef EventPathArray = (CFArrayRef)EventPaths; for( size_t EventIndex = 0; EventIndex < EventCount; ++EventIndex ) { const FSEventStreamEventFlags Flags = EventFlags[EventIndex]; if( !(Flags & kFSEventStreamEventFlagItemIsFile) && !(Flags & kFSEventStreamEventFlagItemIsDir) ) { // events about symlinks don't concern us continue; } auto action = FileSystemAction::Unknown; const bool added = ( Flags & kFSEventStreamEventFlagItemCreated ); const bool renamed = ( Flags & kFSEventStreamEventFlagItemRenamed ); const bool modified = ( Flags & kFSEventStreamEventFlagItemModified ); const bool removed = ( Flags & kFSEventStreamEventFlagItemRemoved ); if (added) { action = FileSystemAction::Create; } if (renamed) { action = FileSystemAction::Rename; } if (modified) { action = FileSystemAction::Modify; } if (removed) { action = FileSystemAction::Delete; } const String resolvedPath = AppleUtils::ToString((CFStringRef)CFArrayGetValueAtIndex(EventPathArray,EventIndex)); macFileSystemWatcher->OnEvent(resolvedPath, action); } } } MacFileSystemWatcher::MacFileSystemWatcher(const String& directory, bool withSubDirs) : FileSystemWatcherBase(directory, withSubDirs) { CFStringRef FullPathMac = AppleUtils::ToString(StringView(directory)); CFArrayRef PathsToWatch = CFArrayCreate(NULL, (const void**)&FullPathMac, 1, NULL); CFAbsoluteTime Latency = 0.2; FSEventStreamContext Context; Context.version = 0; Context.info = this; Context.retain = NULL; Context.release = NULL; Context.copyDescription = NULL; EventStream = FSEventStreamCreate( NULL, &DirectoryWatchCallback, &Context, PathsToWatch, kFSEventStreamEventIdSinceNow, Latency, kFSEventStreamCreateFlagUseCFTypes | kFSEventStreamCreateFlagNoDefer | kFSEventStreamCreateFlagFileEvents ); CFRelease(PathsToWatch); CFRelease(FullPathMac); FSEventStreamScheduleWithRunLoop( EventStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode ); FSEventStreamStart( EventStream ); IsRunning = true; } MacFileSystemWatcher::~MacFileSystemWatcher() { if (IsRunning) { FSEventStreamStop(EventStream); FSEventStreamUnscheduleFromRunLoop(EventStream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode); FSEventStreamInvalidate(EventStream); FSEventStreamRelease(EventStream); } } #endif