QStringList QFSEventsFileSystemWatcherEngine::addPaths(const QStringList &paths, QStringList *files, QStringList *directories) { #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 stop(); wait(); QMutexLocker locker(&mutex); QStringList failedToAdd; // if we have a running FSStreamEvent, we have to kill it, we'll re-add the stream soon. FSEventStreamEventId idToCheck; if (fsStream) { idToCheck = FSEventStreamGetLatestEventId(fsStream); cleanupFSStream(fsStream); } else { idToCheck = kFSEventStreamEventIdSinceNow; } // Brain-dead approach, but works. FSEvents actually can already read sub-trees, but since it's // work to figure out if we are doing a double register, we just register it twice as FSEvents // seems smart enough to only deliver one event. We also duplicate directory entries in here // (e.g., if you watch five files in the same directory, you get that directory included in the // array 5 times). This stupidity also makes remove work correctly though. I'll freely admit // that we could make this a bit smarter. If you do, check the auto-tests, they should catch at // least a couple of the issues. QCFType<CFMutableArrayRef> tmpArray = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); for (int i = 0; i < paths.size(); ++i) { const QString &path = paths.at(i); QFileInfo fileInfo(path); if (!fileInfo.exists()) { failedToAdd.append(path); continue; } if (fileInfo.isDir()) { if (directories->contains(path)) { failedToAdd.append(path); continue; } else { directories->append(path); // Full file path for dirs. QCFString cfpath(createFSStreamPath(fileInfo.canonicalFilePath())); addPathToHash(dirPathInfoHash, cfpath, fileInfo, path); CFArrayAppendValue(tmpArray, cfpath); } } else { if (files->contains(path)) { failedToAdd.append(path); continue; } else { // Just the absolute path (minus it's filename) for files. QCFString cfpath(createFSStreamPath(fileInfo.canonicalPath())); files->append(path); addPathToHash(filePathInfoHash, cfpath, fileInfo, path); CFArrayAppendValue(tmpArray, cfpath); } } } if (!pathsToWatch && failedToAdd.size() == paths.size()) { return failedToAdd; } if (CFArrayGetCount(tmpArray) > 0) { if (pathsToWatch) { CFArrayAppendArray(tmpArray, pathsToWatch, CFRangeMake(0, CFArrayGetCount(pathsToWatch))); CFRelease(pathsToWatch); } pathsToWatch = CFArrayCreateCopy(kCFAllocatorDefault, tmpArray); } FSEventStreamContext context = { 0, this, 0, 0, 0 }; fsStream = FSEventStreamCreate(kCFAllocatorDefault, QFSEventsFileSystemWatcherEngine::fseventsCallback, &context, pathsToWatch, idToCheck, Latency, QtFSEventFlags); warmUpFSEvents(); return failedToAdd; #else Q_UNUSED(paths); Q_UNUSED(files); Q_UNUSED(directories); return QStringList(); #endif }
QStringList QFSEventsFileSystemWatcherEngine::removePaths(const QStringList &paths, QStringList *files, QStringList *directories) { #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 && !defined(Q_OS_IOS) stop(); wait(); QMutexLocker locker(&mutex); // short circuit for smarties that call remove before add and we have nothing. if (pathsToWatch == 0) return paths; QStringList failedToRemove; // if we have a running FSStreamEvent, we have to stop it, we'll re-add the stream soon. FSEventStreamEventId idToCheck; if (fsStream) { idToCheck = FSEventStreamGetLatestEventId(fsStream); cleanupFSStream(fsStream); fsStream = 0; } else { idToCheck = kFSEventStreamEventIdSinceNow; } CFIndex itemCount = CFArrayGetCount(pathsToWatch); QCFType<CFMutableArrayRef> tmpArray = CFArrayCreateMutableCopy(kCFAllocatorDefault, itemCount, pathsToWatch); CFRelease(pathsToWatch); pathsToWatch = 0; for (int i = 0; i < paths.size(); ++i) { // Get the itemCount at the beginning to avoid any overruns during the iteration. itemCount = CFArrayGetCount(tmpArray); const QString &path = paths.at(i); QFileInfo fi(path); QCFString cfpath(createFSStreamPath(fi.canonicalPath())); CFIndex index = CFArrayGetFirstIndexOfValue(tmpArray, CFRangeMake(0, itemCount), cfpath); if (index != -1) { CFArrayRemoveValueAtIndex(tmpArray, index); files->removeAll(path); removePathFromHash(filePathInfoHash, cfpath, path); } else { // Could be a directory we are watching instead. QCFString cfdirpath(createFSStreamPath(fi.canonicalFilePath())); index = CFArrayGetFirstIndexOfValue(tmpArray, CFRangeMake(0, itemCount), cfdirpath); if (index != -1) { CFArrayRemoveValueAtIndex(tmpArray, index); directories->removeAll(path); removePathFromHash(dirPathInfoHash, cfpath, path); } else { failedToRemove.append(path); } } } itemCount = CFArrayGetCount(tmpArray); if (itemCount != 0) { pathsToWatch = CFArrayCreateCopy(kCFAllocatorDefault, tmpArray); FSEventStreamContext context = { 0, this, 0, 0, 0 }; fsStream = FSEventStreamCreate(kCFAllocatorDefault, QFSEventsFileSystemWatcherEngine::fseventsCallback, &context, pathsToWatch, idToCheck, Latency, QtFSEventFlags); warmUpFSEvents(); } return failedToRemove; #else Q_UNUSED(paths); Q_UNUSED(files); Q_UNUSED(directories); return QStringList(); #endif }