예제 #1
0
void WatcherData::notifyCallback(ConstFSEventStreamRef streamRef,
                                 void *clientCallBackInfo,
                                 size_t numEvents,
                                 void *eventPaths,
                                 const FSEventStreamEventFlags eventFlags[],
                                 const FSEventStreamEventId eventIds[])
{
    (void)eventIds;
    WatcherData* watcher = static_cast<WatcherData*>(clientCallBackInfo);
    std::lock_guard<std::mutex> locker(watcher->mutex);
    watcher->since = FSEventStreamGetLatestEventId(streamRef);
    char** paths = reinterpret_cast<char**>(eventPaths);

    FileSystemWatcher *fsWatcher = watcher->watcher;
    {
        std::lock_guard<std::mutex> l(fsWatcher->mMutex);
        for (size_t i = 0; i < numEvents; ++i) {
            const FSEventStreamEventFlags flags = eventFlags[i];
            if (flags & kFSEventStreamEventFlagHistoryDone)
                continue;
            if (flags & kFSEventStreamEventFlagItemIsFile) {
                const Path path(paths[i]);
                if (flags & kFSEventStreamEventFlagItemCreated) {
                    fsWatcher->add(FileSystemWatcher::Add, path);
                }
                if (flags & kFSEventStreamEventFlagItemRemoved) {
                    fsWatcher->add(FileSystemWatcher::Remove, path);
                }
                if (flags & kFSEventStreamEventFlagItemRenamed) {
                    if (path.isFile()) {
                        fsWatcher->add(FileSystemWatcher::Add, path);
                    } else {
                        fsWatcher->add(FileSystemWatcher::Remove, path);
                    }
                }
                if (flags & (kFSEventStreamEventFlagItemModified | kFSEventStreamEventFlagItemInodeMetaMod)) {
                    fsWatcher->add(FileSystemWatcher::Modified, path);
                }
            }
        }
    }

    std::weak_ptr<WatcherData> that = watcher->shared_from_this();
    EventLoop::eventLoop()->callLater([that] {
            if (std::shared_ptr<WatcherData> watcherData = that.lock()) {
                watcherData->watcher->processChanges();
            }
        });
}
void WatcherThread::notifyCallback(ConstFSEventStreamRef streamRef,
                                   void *clientCallBackInfo,
                                   size_t numEvents,
                                   void *eventPaths,
                                   const FSEventStreamEventFlags eventFlags[],
                                   const FSEventStreamEventId eventIds[])
{
    WatcherThread* watcher = static_cast<WatcherThread*>(clientCallBackInfo);
    MutexLocker locker(&watcher->mutex);
    watcher->since = FSEventStreamGetLatestEventId(streamRef);
    char** paths = reinterpret_cast<char**>(eventPaths);
    Set<Path> created, removed, modified;
    for (size_t i = 0; i < numEvents; ++i) {
        const FSEventStreamEventFlags flags = eventFlags[i];
        if (flags & kFSEventStreamEventFlagHistoryDone)
            continue;
        if (flags & kFSEventStreamEventFlagItemIsFile) {
            if (flags & kFSEventStreamEventFlagItemCreated) {
                created.insert(Path(paths[i]));
            } else if (flags & kFSEventStreamEventFlagItemRemoved) {
                removed.insert(Path(paths[i]));
            } else if (flags & (kFSEventStreamEventFlagItemModified
                                | kFSEventStreamEventFlagItemInodeMetaMod)) {
                modified.insert(Path(paths[i]));
            }
        }
    }

    if (!created.empty())
        watcher->receiver->postEvent(new WatcherEvent(WatcherEvent::Created, created));
    if (!removed.empty())
        watcher->receiver->postEvent(new WatcherEvent(WatcherEvent::Removed, removed));
    if (!modified.empty()) {
        watcher->receiver->postEvent(new WatcherEvent(WatcherEvent::Modified, modified));
    }
}
QStringList QFSEventsFileSystemWatcherEngine::removePaths(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);
    // 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
}
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
}