Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qfilesystemwatcher.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
6
7#include <qdatetime.h>
8#include <qdir.h>
9#include <qfileinfo.h>
10#include <qloggingcategory.h>
11#include <qset.h>
12#include <qtimer.h>
13
14#if (defined(Q_OS_LINUX) || defined(Q_OS_QNX)) && QT_CONFIG(inotify)
15#define USE_INOTIFY
16#endif
17
19#if defined(Q_OS_WIN)
21#elif defined(USE_INOTIFY)
23#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) || defined(Q_OS_OPENBSD) || defined(QT_PLATFORM_UIKIT)
25#elif defined(Q_OS_MACOS)
27#endif
28
29#include <algorithm>
30#include <iterator>
31
33
34using namespace Qt::StringLiterals;
35
36Q_LOGGING_CATEGORY(lcWatcher, "qt.core.filesystemwatcher")
37
39{
40#if defined(Q_OS_WIN)
41 return new QWindowsFileSystemWatcherEngine(parent);
42#elif defined(USE_INOTIFY)
43 // there is a chance that inotify may fail on Linux pre-2.6.13 (August
44 // 2005), so we can't just new inotify directly.
46#elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD) || defined(Q_OS_OPENBSD) || defined(QT_PLATFORM_UIKIT)
48#elif defined(Q_OS_MACOS)
50#else
51 Q_UNUSED(parent);
52 return 0;
53#endif
54}
55
60
68
70{
72 native = createNativeEngine(q);
73 if (native) {
75#if defined(Q_OS_WIN)
76 auto *windowsWatcher = static_cast<QWindowsFileSystemWatcherEngine *>(native);
78 QObjectPrivate::connect(windowsWatcher, &WinE::driveLockForRemoval,
79 this, &QFileSystemWatcherPrivate::winDriveLockForRemoval);
80 QObjectPrivate::connect(windowsWatcher, &WinE::driveLockForRemovalFailed,
81 this, &QFileSystemWatcherPrivate::winDriveLockForRemovalFailed);
82 QObjectPrivate::connect(windowsWatcher, &WinE::driveRemoved,
83 this, &QFileSystemWatcherPrivate::winDriveRemoved);
84#endif // Q_OS_WIN
85 }
86}
87
89{
90 if (poller)
91 return;
92
94 poller = new QPollingFileSystemWatcherEngine(q); // that was a mouthful
96}
97
99{
101 qCDebug(lcWatcher) << "file changed" << path << "removed?" << removed << "watching?" << files.contains(path);
102 if (!files.contains(path)) {
103 // the path was removed after a change was detected, but before we delivered the signal
104 return;
105 }
106 if (removed)
107 files.removeAll(path);
108 emit q->fileChanged(path, QFileSystemWatcher::QPrivateSignal());
109}
110
112{
114 qCDebug(lcWatcher) << "directory changed" << path << "removed?" << removed << "watching?" << directories.contains(path);
115 if (!directories.contains(path)) {
116 // perhaps the path was removed after a change was detected, but before we delivered the signal
117 return;
118 }
119 if (removed)
120 directories.removeAll(path);
121 emit q->directoryChanged(path, QFileSystemWatcher::QPrivateSignal());
122}
123
124#if defined(Q_OS_WIN)
125
126void QFileSystemWatcherPrivate::winDriveLockForRemoval(const QString &path)
127{
128 // Windows: Request to lock a (removable/USB) drive for removal, release
129 // its paths under watch, temporarily storing them should the lock fail.
131 QStringList pathsToBeRemoved;
132 auto pred = [&path] (const QString &f) { return !f.startsWith(path, Qt::CaseInsensitive); };
133 std::remove_copy_if(files.cbegin(), files.cend(),
134 std::back_inserter(pathsToBeRemoved), pred);
135 std::remove_copy_if(directories.cbegin(), directories.cend(),
136 std::back_inserter(pathsToBeRemoved), pred);
137 if (!pathsToBeRemoved.isEmpty()) {
138 q->removePaths(pathsToBeRemoved);
139 temporarilyRemovedPaths.insert(path.at(0), pathsToBeRemoved);
140 }
141}
142
143void QFileSystemWatcherPrivate::winDriveLockForRemovalFailed(const QString &path)
144{
145 // Windows: Request to lock a (removable/USB) drive failed (blocked by other
146 // application), restore the watched paths.
148 if (!path.isEmpty()) {
149 const auto it = temporarilyRemovedPaths.find(path.at(0));
150 if (it != temporarilyRemovedPaths.end()) {
151 q->addPaths(it.value());
152 temporarilyRemovedPaths.erase(it);
153 }
154 }
155}
156
157void QFileSystemWatcherPrivate::winDriveRemoved(const QString &path)
158{
159 // Windows: Drive finally removed, clear out paths stored in lock request.
160 if (!path.isEmpty())
161 temporarilyRemovedPaths.remove(path.at(0));
162}
163#endif // Q_OS_WIN
164
221 : QObject(*new QFileSystemWatcherPrivate, parent)
222{
223 d_func()->init();
224}
225
231 : QObject(*new QFileSystemWatcherPrivate, parent)
232{
233 d_func()->init();
235}
236
242
267{
268 if (path.isEmpty()) {
269 qWarning("QFileSystemWatcher::addPath: path is empty");
270 return true;
271 }
272
274 return paths.isEmpty();
275}
276
278{
280 p.reserve(paths.size());
281 const auto isEmpty = [](const QString &s) { return s.isEmpty(); };
282 std::remove_copy_if(paths.begin(), paths.end(),
283 std::back_inserter(p),
284 isEmpty);
285 return p;
286}
287
312{
314
316
317 if (p.isEmpty()) {
318 qWarning("QFileSystemWatcher::addPaths: list is empty");
319 return p;
320 }
321 qCDebug(lcWatcher) << "adding" << paths;
322 const auto selectEngine = [this, d]() -> QFileSystemWatcherEngine* {
323#ifdef QT_BUILD_INTERNAL
324 const QString on = objectName();
325
326 if (Q_UNLIKELY(on.startsWith("_qt_autotest_force_engine_"_L1))) {
327 // Autotest override case - use the explicitly selected engine only
328 const auto forceName = QStringView{on}.mid(26);
329 if (forceName == "poller"_L1) {
330 qCDebug(lcWatcher, "QFileSystemWatcher: skipping native engine, using only polling engine");
331 d_func()->initPollerEngine();
332 return d->poller;
333 } else if (forceName == "native"_L1) {
334 qCDebug(lcWatcher, "QFileSystemWatcher: skipping polling engine, using only native engine");
335 return d->native;
336 }
337 return nullptr;
338 }
339#endif
340 // Normal runtime case - search intelligently for best engine
341 if (d->native) {
342 return d->native;
343 } else {
344 d_func()->initPollerEngine();
345 return d->poller;
346 }
347 };
348
349 if (auto engine = selectEngine())
350 p = engine->addPaths(p, &d->files, &d->directories);
351
352 return p;
353}
354
366{
367 if (path.isEmpty()) {
368 qWarning("QFileSystemWatcher::removePath: path is empty");
369 return true;
370 }
371
373 return paths.isEmpty();
374}
375
388{
390
392
393 if (p.isEmpty()) {
394 qWarning("QFileSystemWatcher::removePaths: list is empty");
395 return p;
396 }
397 qCDebug(lcWatcher) << "removing" << paths;
398
399 if (d->native)
400 p = d->native->removePaths(p, &d->files, &d->directories);
401 if (d->poller)
402 p = d->poller->removePaths(p, &d->files, &d->directories);
403
404 return p;
405}
406
452{
453 Q_D(const QFileSystemWatcher);
454 return d->directories;
455}
456
458{
459 Q_D(const QFileSystemWatcher);
460 return d->files;
461}
462
464
465#include "moc_qfilesystemwatcher.cpp"
466#include "moc_qfilesystemwatcher_p.cpp"
467
void directoryChanged(const QString &path, bool removed)
void fileChanged(const QString &path, bool removed)
void connectEngine(QFileSystemWatcherEngine *e)
void fileChanged(const QString &path, bool removed)
QFileSystemWatcherEngine * native
QFileSystemWatcherEngine * poller
void directoryChanged(const QString &path, bool removed)
QStringList directories() const
Returns a list of paths to directories that are being watched.
QStringList files() const
Returns a list of paths to files that are being watched.
QFileSystemWatcher(QObject *parent=nullptr)
Constructs a new file system watcher object with the given parent.
bool removePath(const QString &file)
Removes the specified path from the file system watcher.
QStringList removePaths(const QStringList &files)
Removes the specified paths from the file system watcher.
bool addPath(const QString &file)
Adds path to the file system watcher if path exists.
~QFileSystemWatcher()
Destroys the file system watcher.
QStringList addPaths(const QStringList &files)
Adds each path in paths to the file system watcher.
static QFseventsFileSystemWatcherEngine * create(QObject *parent)
static QInotifyFileSystemWatcherEngine * create(QObject *parent)
static QKqueueFileSystemWatcherEngine * create(QObject *parent)
static QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer< Func1 >::Object *sender, Func1 signal, const typename QtPrivate::FunctionPointer< Func2 >::Object *receiverPrivate, Func2 slot, Qt::ConnectionType type=Qt::AutoConnection)
Definition qobject_p.h:299
\inmodule QtCore
Definition qobject.h:103
QString objectName
the name of this object
Definition qobject.h:107
iterator end()
Definition qset.h:140
iterator find(const T &value)
Definition qset.h:159
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:78
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5455
QString mid(qsizetype position, qsizetype n=-1) const &
Definition qstring.cpp:5300
QSet< QString >::iterator it
Combined button and popup list for selecting options.
@ CaseInsensitive
#define Q_UNLIKELY(x)
QList< QString > QStringList
Constructs a string list that contains the given string, str.
static QStringList empty_paths_pruned(const QStringList &paths)
#define qWarning
Definition qlogging.h:166
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
GLfloat GLfloat f
GLsizei const GLuint * paths
GLdouble s
[6]
Definition qopenglext.h:235
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLsizei const GLchar *const * path
GLfloat GLfloat p
[1]
#define emit
#define Q_UNUSED(x)
QObject::connect nullptr
QJSEngine engine
[0]