Remove OS X FSEvents watcher.
Per QTBUG-9249, this backend is buggy, and not recommended for use by Apple. Change-Id: I72ce88006a4badbbfdd825717020078778d16a36 Reviewed-by: Sergio Ahumada <sergio.ahumada@nokia.com>
This commit is contained in:
parent
8ad583b7f9
commit
848f53a268
@ -92,9 +92,8 @@ win32 {
|
||||
io/qfilesystemiterator_unix.cpp \
|
||||
|
||||
!nacl:macx-*: {
|
||||
HEADERS += io/qfilesystemwatcher_fsevents_p.h
|
||||
SOURCES += io/qfilesystemengine_mac.cpp
|
||||
SOURCES += io/qsettings_mac.cpp io/qfilesystemwatcher_fsevents.cpp
|
||||
SOURCES += io/qsettings_mac.cpp
|
||||
}
|
||||
macx-*: {
|
||||
SOURCES += io/qstandardpaths_mac.cpp
|
||||
|
@ -59,9 +59,6 @@
|
||||
#elif defined(Q_OS_LINUX)
|
||||
# include "qfilesystemwatcher_inotify_p.h"
|
||||
#elif defined(Q_OS_FREEBSD) || defined(Q_OS_MAC)
|
||||
# if (defined Q_OS_MAC) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
|
||||
# include "qfilesystemwatcher_fsevents_p.h"
|
||||
# endif //MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
|
||||
# include "qfilesystemwatcher_kqueue_p.h"
|
||||
#endif
|
||||
|
||||
@ -76,12 +73,7 @@ QFileSystemWatcherEngine *QFileSystemWatcherPrivate::createNativeEngine()
|
||||
// 2005), so we can't just new inotify directly.
|
||||
return QInotifyFileSystemWatcherEngine::create();
|
||||
#elif defined(Q_OS_FREEBSD) || defined(Q_OS_MAC)
|
||||
# if 0 && defined(Q_OS_MAC) && (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5)
|
||||
if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5)
|
||||
return QFSEventsFileSystemWatcherEngine::create();
|
||||
else
|
||||
# endif
|
||||
return QKqueueFileSystemWatcherEngine::create();
|
||||
return QKqueueFileSystemWatcherEngine::create();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
|
@ -1,492 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#define _DARWIN_USE_64_BIT_INODE
|
||||
#include <qplatformdefs.h>
|
||||
|
||||
#include "qfilesystemwatcher.h"
|
||||
#include "qfilesystemwatcher_fsevents_p.h"
|
||||
|
||||
#ifndef QT_NO_FILESYSTEMWATCHER
|
||||
|
||||
#include <qdebug.h>
|
||||
#include <qfile.h>
|
||||
#include <qdatetime.h>
|
||||
#include <qfileinfo.h>
|
||||
#include <qvarlengtharray.h>
|
||||
|
||||
#include <mach/mach.h>
|
||||
#include <sys/types.h>
|
||||
#include <CoreFoundation/CFRunLoop.h>
|
||||
#include <CoreFoundation/CFUUID.h>
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#include <AvailabilityMacros.h>
|
||||
#include <private/qcore_mac_p.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
|
||||
// Static operator overloading so for the sake of some convieniece.
|
||||
// They only live in this compilation unit to avoid polluting Qt in general.
|
||||
static bool operator==(const struct ::timespec &left, const struct ::timespec &right)
|
||||
{
|
||||
return left.tv_sec == right.tv_sec
|
||||
&& left.tv_nsec == right.tv_nsec;
|
||||
}
|
||||
|
||||
static bool operator==(const struct ::stat &left, const struct ::stat &right)
|
||||
{
|
||||
return left.st_dev == right.st_dev
|
||||
&& left.st_mode == right.st_mode
|
||||
&& left.st_size == right.st_size
|
||||
&& left.st_ino == right.st_ino
|
||||
&& left.st_uid == right.st_uid
|
||||
&& left.st_gid == right.st_gid
|
||||
&& left.st_mtimespec == right.st_mtimespec
|
||||
&& left.st_ctimespec == right.st_ctimespec
|
||||
&& left.st_flags == right.st_flags;
|
||||
}
|
||||
|
||||
static bool operator!=(const struct ::stat &left, const struct ::stat &right)
|
||||
{
|
||||
return !(operator==(left, right));
|
||||
}
|
||||
|
||||
|
||||
static void addPathToHash(PathHash &pathHash, const QString &key, const QFileInfo &fileInfo,
|
||||
const QString &path)
|
||||
{
|
||||
PathInfoList &list = pathHash[key];
|
||||
list.push_back(PathInfo(path,
|
||||
fileInfo.canonicalFilePath().normalized(QString::NormalizationForm_D).toUtf8()));
|
||||
pathHash.insert(key, list);
|
||||
}
|
||||
|
||||
static void removePathFromHash(PathHash &pathHash, const QString &key, const QString &path)
|
||||
{
|
||||
PathInfoList &list = pathHash[key];
|
||||
// We make the assumption that the list contains unique paths
|
||||
PathInfoList::iterator End = list.end();
|
||||
PathInfoList::iterator it = list.begin();
|
||||
while (it != End) {
|
||||
if (it->originalPath == path) {
|
||||
list.erase(it);
|
||||
break;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
if (list.isEmpty())
|
||||
pathHash.remove(key);
|
||||
}
|
||||
|
||||
static void stopFSStream(FSEventStreamRef stream)
|
||||
{
|
||||
if (stream) {
|
||||
FSEventStreamStop(stream);
|
||||
FSEventStreamInvalidate(stream);
|
||||
}
|
||||
}
|
||||
|
||||
static QString createFSStreamPath(const QString &absolutePath)
|
||||
{
|
||||
// The path returned has a trailing slash, so ensure that here.
|
||||
QString string = absolutePath;
|
||||
string.reserve(string.size() + 1);
|
||||
string.append(QLatin1Char('/'));
|
||||
return string;
|
||||
}
|
||||
|
||||
static void cleanupFSStream(FSEventStreamRef stream)
|
||||
{
|
||||
if (stream)
|
||||
FSEventStreamRelease(stream);
|
||||
}
|
||||
|
||||
const FSEventStreamCreateFlags QtFSEventFlags = (kFSEventStreamCreateFlagUseCFTypes | kFSEventStreamCreateFlagNoDefer /* | kFSEventStreamCreateFlagWatchRoot*/);
|
||||
|
||||
const CFTimeInterval Latency = 0.033; // This will do updates 30 times a second which is probably more than you need.
|
||||
#endif
|
||||
|
||||
QFSEventsFileSystemWatcherEngine::QFSEventsFileSystemWatcherEngine()
|
||||
: fsStream(0), pathsToWatch(0), threadsRunLoop(0)
|
||||
{
|
||||
}
|
||||
|
||||
QFSEventsFileSystemWatcherEngine::~QFSEventsFileSystemWatcherEngine()
|
||||
{
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
|
||||
// I assume that at this point, QFileSystemWatcher has already called stop
|
||||
// on me, so I don't need to invalidate or stop my stream, simply
|
||||
// release it.
|
||||
cleanupFSStream(fsStream);
|
||||
if (pathsToWatch)
|
||||
CFRelease(pathsToWatch);
|
||||
#endif
|
||||
}
|
||||
|
||||
QFSEventsFileSystemWatcherEngine *QFSEventsFileSystemWatcherEngine::create()
|
||||
{
|
||||
return new QFSEventsFileSystemWatcherEngine();
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
void QFSEventsFileSystemWatcherEngine::warmUpFSEvents()
|
||||
{
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
|
||||
// This function assumes that the mutex has already been grabbed before calling it.
|
||||
// It exits with the mutex still locked (Q_ASSERT(mutex.isLocked()) ;-).
|
||||
start();
|
||||
waitCondition.wait(&mutex);
|
||||
#endif
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
|
||||
void QFSEventsFileSystemWatcherEngine::updateList(PathInfoList &list, bool directory, bool emitSignals)
|
||||
{
|
||||
PathInfoList::iterator End = list.end();
|
||||
PathInfoList::iterator it = list.begin();
|
||||
while (it != End) {
|
||||
struct ::stat newInfo;
|
||||
if (::stat(it->absolutePath, &newInfo) == 0) {
|
||||
if (emitSignals) {
|
||||
if (newInfo != it->savedInfo) {
|
||||
it->savedInfo = newInfo;
|
||||
if (directory)
|
||||
emit directoryChanged(it->originalPath, false);
|
||||
else
|
||||
emit fileChanged(it->originalPath, false);
|
||||
}
|
||||
} else {
|
||||
it->savedInfo = newInfo;
|
||||
}
|
||||
} else {
|
||||
if (errno == ENOENT) {
|
||||
if (emitSignals) {
|
||||
if (directory)
|
||||
emit directoryChanged(it->originalPath, true);
|
||||
else
|
||||
emit fileChanged(it->originalPath, true);
|
||||
}
|
||||
it = list.erase(it);
|
||||
continue;
|
||||
} else {
|
||||
qWarning("%s:%d:QFSEventsFileSystemWatcherEngine: stat error on %s:%s",
|
||||
__FILE__, __LINE__, qPrintable(it->originalPath), strerror(errno));
|
||||
|
||||
}
|
||||
}
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void QFSEventsFileSystemWatcherEngine::updateHash(PathHash &pathHash)
|
||||
{
|
||||
PathHash::iterator HashEnd = pathHash.end();
|
||||
PathHash::iterator it = pathHash.begin();
|
||||
const bool IsDirectory = (&pathHash == &dirPathInfoHash);
|
||||
while (it != HashEnd) {
|
||||
updateList(it.value(), IsDirectory, false);
|
||||
if (it.value().isEmpty())
|
||||
it = pathHash.erase(it);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void QFSEventsFileSystemWatcherEngine::fseventsCallback(ConstFSEventStreamRef ,
|
||||
void *clientCallBackInfo, size_t numEvents,
|
||||
void *eventPaths,
|
||||
const FSEventStreamEventFlags eventFlags[],
|
||||
const FSEventStreamEventId [])
|
||||
{
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
|
||||
QFSEventsFileSystemWatcherEngine *watcher = static_cast<QFSEventsFileSystemWatcherEngine *>(clientCallBackInfo);
|
||||
QMutexLocker locker(&watcher->mutex);
|
||||
CFArrayRef paths = static_cast<CFArrayRef>(eventPaths);
|
||||
for (size_t i = 0; i < numEvents; ++i) {
|
||||
const QString path = QCFString::toQString(
|
||||
static_cast<CFStringRef>(CFArrayGetValueAtIndex(paths, i)));
|
||||
const FSEventStreamEventFlags pathFlags = eventFlags[i];
|
||||
// There are several flags that may be passed, but we really don't care about them ATM.
|
||||
// Here they are and why we don't care.
|
||||
// kFSEventStreamEventFlagHistoryDone--(very unlikely to be gotten, but even then, not much changes).
|
||||
// kFSEventStreamEventFlagMustScanSubDirs--Likely means the data is very much out of date, we
|
||||
// aren't coalescing our directories, so again not so much of an issue
|
||||
// kFSEventStreamEventFlagRootChanged | kFSEventStreamEventFlagMount | kFSEventStreamEventFlagUnmount--
|
||||
// These three flags indicate something has changed, but the stat will likely show this, so
|
||||
// there's not really much to worry about.
|
||||
// (btw, FSEvents is not the correct way of checking for mounts/unmounts,
|
||||
// there are real CarbonCore events for that.)
|
||||
Q_UNUSED(pathFlags);
|
||||
if (watcher->filePathInfoHash.contains(path))
|
||||
watcher->updateList(watcher->filePathInfoHash[path], false, true);
|
||||
|
||||
if (watcher->dirPathInfoHash.contains(path))
|
||||
watcher->updateList(watcher->dirPathInfoHash[path], true, true);
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(clientCallBackInfo);
|
||||
Q_UNUSED(numEvents);
|
||||
Q_UNUSED(eventPaths);
|
||||
Q_UNUSED(eventFlags);
|
||||
#endif
|
||||
}
|
||||
|
||||
void QFSEventsFileSystemWatcherEngine::stop()
|
||||
{
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
|
||||
QMutexLocker locker(&mutex);
|
||||
stopFSStream(fsStream);
|
||||
if (threadsRunLoop) {
|
||||
CFRunLoopStop(threadsRunLoop);
|
||||
waitForStop.wait(&mutex);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void QFSEventsFileSystemWatcherEngine::updateFiles()
|
||||
{
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
|
||||
QMutexLocker locker(&mutex);
|
||||
updateHash(filePathInfoHash);
|
||||
updateHash(dirPathInfoHash);
|
||||
if (filePathInfoHash.isEmpty() && dirPathInfoHash.isEmpty()) {
|
||||
// Everything disappeared before we got to start, don't bother.
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
|
||||
// Code duplicated from stop(), with the exception that we
|
||||
// don't wait on waitForStop here. Doing this will lead to
|
||||
// a deadlock since this function is called from the worker
|
||||
// thread. (waitForStop.wakeAll() is only called from the
|
||||
// end of run()).
|
||||
stopFSStream(fsStream);
|
||||
if (threadsRunLoop)
|
||||
CFRunLoopStop(threadsRunLoop);
|
||||
#endif
|
||||
cleanupFSStream(fsStream);
|
||||
}
|
||||
waitCondition.wakeAll();
|
||||
#endif
|
||||
}
|
||||
|
||||
void QFSEventsFileSystemWatcherEngine::run()
|
||||
{
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
|
||||
threadsRunLoop = CFRunLoopGetCurrent();
|
||||
FSEventStreamScheduleWithRunLoop(fsStream, threadsRunLoop, kCFRunLoopDefaultMode);
|
||||
bool startedOK = FSEventStreamStart(fsStream);
|
||||
// It's recommended by Apple that you only update the files after you've started
|
||||
// the stream, because otherwise you might miss an update in between starting it.
|
||||
updateFiles();
|
||||
#ifdef QT_NO_DEBUG
|
||||
Q_UNUSED(startedOK);
|
||||
#else
|
||||
Q_ASSERT(startedOK);
|
||||
#endif
|
||||
// If for some reason we called stop up above (and invalidated our stream), this call will return
|
||||
// immediately.
|
||||
CFRunLoopRun();
|
||||
threadsRunLoop = 0;
|
||||
QMutexLocker locker(&mutex);
|
||||
waitForStop.wakeAll();
|
||||
#endif
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
#endif //QT_NO_FILESYSTEMWATCHER
|
@ -1,132 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the QtCore module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#ifndef FILEWATCHER_FSEVENTS_P_H
|
||||
#define FILEWATCHER_FSEVENTS_P_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists for the convenience
|
||||
// of the QLibrary class. This header file may change from
|
||||
// version to version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include "qfilesystemwatcher_p.h"
|
||||
|
||||
#ifndef QT_NO_FILESYSTEMWATCHER
|
||||
|
||||
#include <QtCore/qmutex.h>
|
||||
#include <QtCore/qwaitcondition.h>
|
||||
#include <QtCore/qthread.h>
|
||||
#include <QtCore/qhash.h>
|
||||
#include <QtCore/qlinkedlist.h>
|
||||
#include <private/qcore_mac_p.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
typedef struct __FSEventStream *FSEventStreamRef;
|
||||
typedef const struct __FSEventStream *ConstFSEventStreamRef;
|
||||
typedef const struct __CFArray *CFArrayRef;
|
||||
typedef UInt32 FSEventStreamEventFlags;
|
||||
typedef uint64_t FSEventStreamEventId;
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
|
||||
// Yes, I use a stat element here. QFileInfo requires too much knowledge about implementation
|
||||
// details to be used as a long-standing record. Since I'm going to have to store this information, I can
|
||||
// do the stat myself too.
|
||||
struct PathInfo {
|
||||
PathInfo(const QString &path, const QByteArray &absPath)
|
||||
: originalPath(path), absolutePath(absPath) {}
|
||||
QString originalPath; // The path we need to emit
|
||||
QByteArray absolutePath; // The path we need to stat.
|
||||
struct ::stat savedInfo; // All the info for the path so we can compare it.
|
||||
};
|
||||
typedef QLinkedList<PathInfo> PathInfoList;
|
||||
typedef QHash<QString, PathInfoList> PathHash;
|
||||
#endif
|
||||
|
||||
class QFSEventsFileSystemWatcherEngine : public QFileSystemWatcherEngine
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
~QFSEventsFileSystemWatcherEngine();
|
||||
|
||||
static QFSEventsFileSystemWatcherEngine *create();
|
||||
|
||||
QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories);
|
||||
QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories);
|
||||
|
||||
void stop();
|
||||
|
||||
private:
|
||||
QFSEventsFileSystemWatcherEngine();
|
||||
void warmUpFSEvents();
|
||||
void updateFiles();
|
||||
|
||||
static void fseventsCallback(ConstFSEventStreamRef streamRef, void *clientCallBackInfo, size_t numEvents,
|
||||
void *eventPaths, const FSEventStreamEventFlags eventFlags[],
|
||||
const FSEventStreamEventId eventIds[]);
|
||||
void run();
|
||||
FSEventStreamRef fsStream;
|
||||
CFArrayRef pathsToWatch;
|
||||
CFRunLoopRef threadsRunLoop;
|
||||
QMutex mutex;
|
||||
QWaitCondition waitCondition;
|
||||
QWaitCondition waitForStop;
|
||||
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
|
||||
PathHash filePathInfoHash;
|
||||
PathHash dirPathInfoHash;
|
||||
void updateHash(PathHash &pathHash);
|
||||
void updateList(PathInfoList &list, bool directory, bool emitSignals);
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif //QT_NO_FILESYSTEMWATCHER
|
||||
|
||||
#endif
|
||||
|
||||
QT_END_NAMESPACE
|
Loading…
Reference in New Issue
Block a user