4#include <qplatformdefs.h>
9#include "private/qcore_unix_p.h"
21# define DEBUG if (true) qDebug
23# define DEBUG if (false) qDebug
29 void *clientCallBackInfo,
32 const FSEventStreamEventFlags eventFlags[],
33 const FSEventStreamEventId eventIds[])
37 char **
paths =
static_cast<char **
>(eventPaths);
39 engine->processEvent(streamRef, numEvents,
paths, eventFlags, eventIds);
42bool QFseventsFileSystemWatcherEngine::checkDir(DirsByName::iterator &
it)
44 bool needsRestart =
false;
51 needsRestart |= derefPath(
info.watchedPath);
53 it = watchingState.watchedDirectories.erase(
it);
54 }
else if (st.st_ctimespec !=
info.ctime || st.st_mode !=
info.mode) {
55 info.ctime = st.st_ctimespec;
56 info.mode = st.st_mode;
60 bool dirChanged =
false;
61 InfoByName &entries =
it->entries;
69 if (
i->ctime != st.st_ctimespec ||
i->mode != st.st_mode) {
72 i->ctime = st.st_ctimespec;
80 while (dirIt.hasNext()) {
82 QString entryName = dirIt.filePath();
83 if (!entries.contains(entryName)) {
100bool QFseventsFileSystemWatcherEngine::rescanDirs(
const QString &
path)
102 bool needsRestart =
false;
105 it != watchingState.watchedDirectories.end(); ) {
106 if (
it.key().startsWith(
path))
107 needsRestart |= checkDir(
it);
115bool QFseventsFileSystemWatcherEngine::rescanFiles(InfoByName &filesInPath)
117 bool needsRestart =
false;
124 needsRestart |= derefPath(
it->watchedPath);
128 }
else if (st.st_ctimespec !=
it->ctime || st.st_mode !=
it->mode) {
129 it->ctime = st.st_ctimespec;
130 it->mode = st.st_mode;
140bool QFseventsFileSystemWatcherEngine::rescanFiles(
const QString &
path)
142 bool needsRestart =
false;
145 i != watchingState.watchedFiles.end(); ) {
146 if (
i.key().startsWith(
path)) {
147 needsRestart |= rescanFiles(
i.value());
148 if (
i.value().isEmpty()) {
149 i = watchingState.watchedFiles.erase(
i);
163 const FSEventStreamEventFlags eventFlags[],
164 const FSEventStreamEventId eventIds[])
166#if defined(Q_OS_MACOS)
169 bool needsRestart =
false;
173 for (
size_t i = 0;
i < numEvents; ++
i) {
174 FSEventStreamEventFlags eFlags = eventFlags[
i];
175 DEBUG(
"Change %llu in %s, flags %x", eventIds[
i], eventPaths[
i], (
unsigned int)eFlags);
177 if (eFlags & kFSEventStreamEventFlagEventIdsWrapped) {
178 DEBUG(
"\tthe event ids wrapped");
179 lastReceivedEvent = 0;
181 lastReceivedEvent =
qMax(lastReceivedEvent, eventIds[
i]);
187 if (eFlags & kFSEventStreamEventFlagMustScanSubDirs) {
188 DEBUG(
"\tmust rescan directory because of coalesced events");
189 if (eFlags & kFSEventStreamEventFlagUserDropped)
190 DEBUG(
"\t\t... user dropped.");
191 if (eFlags & kFSEventStreamEventFlagKernelDropped)
192 DEBUG(
"\t\t... kernel dropped.");
193 needsRestart |= rescanDirs(
path);
194 needsRestart |= rescanFiles(
path);
198 if (eFlags & kFSEventStreamEventFlagRootChanged) {
201 if (dirIt != watchingState.watchedDirectories.end())
202 needsRestart |= checkDir(dirIt);
203 needsRestart |= rescanFiles(
path);
207 if ((eFlags & kFSEventStreamEventFlagItemIsDir) && (eFlags & kFSEventStreamEventFlagItemRemoved))
208 needsRestart |= rescanDirs(
path);
212 if (dirIt != watchingState.watchedDirectories.end())
213 needsRestart |= checkDir(dirIt);
217 if (pIt != watchingState.watchedFiles.end())
218 needsRestart |= rescanFiles(pIt.value());
232void QFseventsFileSystemWatcherEngine::doEmitFileChanged(
const QString &
path,
bool removed)
234 DEBUG() <<
"emitting fileChanged for" <<
path <<
"with removed =" << removed;
238void QFseventsFileSystemWatcherEngine::doEmitDirectoryChanged(
const QString &
path,
bool removed)
240 DEBUG() <<
"emitting directoryChanged for" <<
path <<
"with removed =" << removed;
244bool QFseventsFileSystemWatcherEngine::restartStream()
248 return startStream();
256QFseventsFileSystemWatcherEngine::QFseventsFileSystemWatcherEngine(
QObject *
parent)
259 , lastReceivedEvent(kFSEventStreamEventIdSinceNow)
271 queue = dispatch_queue_create(
"org.qt-project.QFseventsFileSystemWatcherEngine", NULL);
278 dispatch_sync(queue, ^{
281 FSEventStreamStop(
stream);
288 dispatch_release(queue);
298 DEBUG(
"Flushing, last id is %llu", FSEventStreamGetLatestEventId(
stream));
299 FSEventStreamFlushSync(
stream);
304 bool wasRunning =
stream !=
nullptr;
305 bool needsRestart =
false;
307 WatchingState oldState = watchingState;
315 QString watchedPath, parentPath;
326 const bool isDir = S_ISDIR(st.st_mode);
328 if (watchingState.watchedDirectories.contains(realPath))
330 directories->append(origPath);
331 watchedPath = realPath;
333 if (
files->contains(origPath))
335 files->append(origPath);
338 parentPath = watchedPath;
344 ei = watchingState.watchedPaths.end();
i != ei; ++
i) {
346 watchedPath =
i.key();
352 if (
it == watchingState.watchedPaths.end()) {
354 watchingState.watchedPaths.insert(watchedPath, 1);
360 Info info(origPath, st.st_ctimespec, st.st_mode, watchedPath);
363 dirInfo.dirInfo =
info;
364 dirInfo.entries = scanForDirEntries(realPath);
365 watchingState.watchedDirectories.insert(realPath, dirInfo);
366 DEBUG(
"-- Also adding '%s' to watchedDirectories",
qPrintable(realPath));
368 watchingState.watchedFiles[parentPath].insert(realPath,
info);
375 if (!startStream()) {
377 watchingState = std::move(oldState);
397 bool needsRestart =
false;
399 WatchingState oldState = watchingState;
402 auto sg =
qScopeGuard([&]{ unhandled.push_back(origPath); });
412 if (dirIt != watchingState.watchedDirectories.end()) {
413 needsRestart |= derefPath(dirIt->dirInfo.watchedPath);
414 watchingState.watchedDirectories.erase(dirIt);
415 directories->removeAll(origPath);
423 if (pIt != watchingState.watchedFiles.end()) {
426 if (fIt != filesInDir.
end()) {
427 needsRestart |= derefPath(fIt->watchedPath);
428 filesInDir.
erase(fIt);
430 watchingState.watchedFiles.erase(pIt);
431 files->removeAll(origPath);
442 if (!restartStream()) {
443 watchingState = std::move(oldState);
453bool QFseventsFileSystemWatcherEngine::startStream()
461 if (watchingState.watchedPaths.isEmpty())
464 DEBUG() <<
"Starting stream with paths" << watchingState.watchedPaths.keys();
466 NSMutableArray<NSString *> *pathsToWatch = [NSMutableArray<NSString *> arrayWithCapacity:watchingState.watchedPaths.size()];
468 [pathsToWatch addObject:
i.
key().toNSString()];
470 struct FSEventStreamContext callBackInfo = {
477 const CFAbsoluteTime latency = .5;
481 if (lastReceivedEvent == kFSEventStreamEventIdSinceNow)
482 lastReceivedEvent = FSEventsGetCurrentEventId();
483 stream = FSEventStreamCreate(NULL,
486 reinterpret_cast<CFArrayRef
>(pathsToWatch),
489 FSEventStreamCreateFlags(0));
492 DEBUG() <<
"Failed to create stream!";
498 if (FSEventStreamStart(
stream)) {
499 DEBUG() <<
"Stream started successfully with sinceWhen =" << lastReceivedEvent;
502 DEBUG() <<
"Stream failed to start!";
503 FSEventStreamInvalidate(
stream);
504 FSEventStreamRelease(
stream);
510void QFseventsFileSystemWatcherEngine::stopStream(
bool isStopped)
515 FSEventStreamStop(
stream);
516 FSEventStreamInvalidate(
stream);
517 FSEventStreamRelease(
stream);
519 DEBUG() <<
"Stream stopped. Last event ID:" << lastReceivedEvent;
528 while (
it.hasNext()) {
540bool QFseventsFileSystemWatcherEngine::derefPath(
const QString &watchedPath)
543 if (
it != watchingState.watchedPaths.end() && --
it.value() < 1) {
544 watchingState.watchedPaths.erase(
it);
The QDirIterator class provides an iterator for directory entrylists.
static QChar separator()
Returns the native directory separator: "/" under Unix and "\\" under Windows.
\inmodule QtCore \reentrant
bool isDir() const
Returns true if this object points to a directory or to a symbolic link to a directory.
QString canonicalFilePath() const
Returns the canonical path including the file name, i.e.
QString path() const
Returns the file's path.
void directoryChanged(const QString &path, bool removed)
void fileChanged(const QString &path, bool removed)
static QByteArray encodeName(const QString &fileName)
Converts fileName to an 8-bit encoding that you can use in native APIs.
static QString decodeName(const QByteArray &localFileName)
This does the reverse of QFile::encodeName() using localFileName.
void processEvent(ConstFSEventStreamRef streamRef, size_t numEvents, char **eventPaths, const FSEventStreamEventFlags eventFlags[], const FSEventStreamEventId eventIds[])
QStringList removePaths(const QStringList &paths, QStringList *files, QStringList *directories)
void emitDirectoryChanged(const QString &path, bool removed)
QStringList addPaths(const QStringList &paths, QStringList *files, QStringList *directories)
void scheduleStreamRestart()
static QFseventsFileSystemWatcherEngine * create(QObject *parent)
~QFseventsFileSystemWatcherEngine()
void emitFileChanged(const QString &path, bool removed)
iterator find(const Key &key)
Returns an iterator pointing to the item with the key in the hash.
iterator erase(const_iterator it)
T value(const Key &key) const noexcept
iterator end() noexcept
Returns an \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the last ...
friend class const_iterator
bool isEmpty() const noexcept
Returns true if the hash contains no items; otherwise returns false.
void unlock() noexcept
Unlocks this mutex locker.
QObject * parent() const
Returns a pointer to the parent object.
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
iterator erase(const_iterator i)
iterator insert(const T &value)
\macro QT_RESTRICTED_CAST_FROM_ASCII
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
void chop(qsizetype n)
Removes n characters from the end of the string.
qsizetype size() const
Returns the number of characters in this string.
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
QString & insert(qsizetype i, QChar c)
QString normalized(NormalizationForm mode, QChar::UnicodeVersion version=QChar::Unicode_Unassigned) const
Returns the string in the given Unicode normalization mode, according to the given version of the Uni...
QSet< QString >::iterator it
Combined button and popup list for selecting options.
static QT_BEGIN_NAMESPACE void callBackFunction(ConstFSEventStreamRef streamRef, void *clientCallBackInfo, size_t numEvents, void *eventPaths, const FSEventStreamEventFlags eventFlags[], const FSEventStreamEventId eventIds[])
constexpr const T & qMax(const T &a, const T &b)
GLsizei const GLuint * paths
GLsizei const GLchar *const * path
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
#define qPrintable(string)
QFileInfo info(fileName)
[8]
QFileInfo fi("c:/temp/foo")
[newstuff]
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent