Qt 6.x
The Qt SDK
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)
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
52 return 0;
53#endif
54}
55
57 : native(nullptr), poller(nullptr)
58{
59}
60
62{
64 native = createNativeEngine(q);
65 if (native) {
67 SIGNAL(fileChanged(QString,bool)),
68 q,
71 SIGNAL(directoryChanged(QString,bool)),
72 q,
74#if defined(Q_OS_WIN)
77 q, [this] (const QString &p) { _q_winDriveLockForRemoval(p); });
80 q, [this] (const QString &p) { _q_winDriveLockForRemovalFailed(p); });
83 q, [this] (const QString &p) { _q_winDriveRemoved(p); });
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 SIGNAL(fileChanged(QString,bool)),
97 q,
100 SIGNAL(directoryChanged(QString,bool)),
101 q,
103}
104
106{
108 qCDebug(lcWatcher) << "file changed" << path << "removed?" << removed << "watching?" << files.contains(path);
109 if (!files.contains(path)) {
110 // the path was removed after a change was detected, but before we delivered the signal
111 return;
112 }
113 if (removed)
114 files.removeAll(path);
115 emit q->fileChanged(path, QFileSystemWatcher::QPrivateSignal());
116}
117
119{
121 qCDebug(lcWatcher) << "directory changed" << path << "removed?" << removed << "watching?" << directories.contains(path);
122 if (!directories.contains(path)) {
123 // perhaps the path was removed after a change was detected, but before we delivered the signal
124 return;
125 }
126 if (removed)
127 directories.removeAll(path);
128 emit q->directoryChanged(path, QFileSystemWatcher::QPrivateSignal());
129}
130
131#if defined(Q_OS_WIN)
132
133void QFileSystemWatcherPrivate::_q_winDriveLockForRemoval(const QString &path)
134{
135 // Windows: Request to lock a (removable/USB) drive for removal, release
136 // its paths under watch, temporarily storing them should the lock fail.
138 QStringList pathsToBeRemoved;
139 auto pred = [&path] (const QString &f) { return !f.startsWith(path, Qt::CaseInsensitive); };
140 std::remove_copy_if(files.cbegin(), files.cend(),
141 std::back_inserter(pathsToBeRemoved), pred);
142 std::remove_copy_if(directories.cbegin(), directories.cend(),
143 std::back_inserter(pathsToBeRemoved), pred);
144 if (!pathsToBeRemoved.isEmpty()) {
145 q->removePaths(pathsToBeRemoved);
146 temporarilyRemovedPaths.insert(path.at(0), pathsToBeRemoved);
147 }
148}
149
150void QFileSystemWatcherPrivate::_q_winDriveLockForRemovalFailed(const QString &path)
151{
152 // Windows: Request to lock a (removable/USB) drive failed (blocked by other
153 // application), restore the watched paths.
155 if (!path.isEmpty()) {
156 const auto it = temporarilyRemovedPaths.find(path.at(0));
157 if (it != temporarilyRemovedPaths.end()) {
158 q->addPaths(it.value());
159 temporarilyRemovedPaths.erase(it);
160 }
161 }
162}
163
164void QFileSystemWatcherPrivate::_q_winDriveRemoved(const QString &path)
165{
166 // Windows: Drive finally removed, clear out paths stored in lock request.
167 if (!path.isEmpty())
168 temporarilyRemovedPaths.remove(path.at(0));
169}
170#endif // Q_OS_WIN
171
229{
230 d_func()->init();
231}
232
239{
240 d_func()->init();
242}
243
248{ }
249
274{
275 if (path.isEmpty()) {
276 qWarning("QFileSystemWatcher::addPath: path is empty");
277 return true;
278 }
279
281 return paths.isEmpty();
282}
283
285{
287 p.reserve(paths.size());
288 const auto isEmpty = [](const QString &s) { return s.isEmpty(); };
289 std::remove_copy_if(paths.begin(), paths.end(),
290 std::back_inserter(p),
291 isEmpty);
292 return p;
293}
294
319{
321
323
324 if (p.isEmpty()) {
325 qWarning("QFileSystemWatcher::addPaths: list is empty");
326 return p;
327 }
328 qCDebug(lcWatcher) << "adding" << paths;
329 const auto selectEngine = [this, d]() -> QFileSystemWatcherEngine* {
330#ifdef QT_BUILD_INTERNAL
331 const QString on = objectName();
332
333 if (Q_UNLIKELY(on.startsWith("_qt_autotest_force_engine_"_L1))) {
334 // Autotest override case - use the explicitly selected engine only
335 const auto forceName = QStringView{on}.mid(26);
336 if (forceName == "poller"_L1) {
337 qCDebug(lcWatcher, "QFileSystemWatcher: skipping native engine, using only polling engine");
338 d_func()->initPollerEngine();
339 return d->poller;
340 } else if (forceName == "native"_L1) {
341 qCDebug(lcWatcher, "QFileSystemWatcher: skipping polling engine, using only native engine");
342 return d->native;
343 }
344 return nullptr;
345 }
346#endif
347 // Normal runtime case - search intelligently for best engine
348 if (d->native) {
349 return d->native;
350 } else {
351 d_func()->initPollerEngine();
352 return d->poller;
353 }
354 };
355
356 if (auto engine = selectEngine())
357 p = engine->addPaths(p, &d->files, &d->directories);
358
359 return p;
360}
361
373{
374 if (path.isEmpty()) {
375 qWarning("QFileSystemWatcher::removePath: path is empty");
376 return true;
377 }
378
380 return paths.isEmpty();
381}
382
395{
397
399
400 if (p.isEmpty()) {
401 qWarning("QFileSystemWatcher::removePaths: list is empty");
402 return p;
403 }
404 qCDebug(lcWatcher) << "removing" << paths;
405
406 if (d->native)
407 p = d->native->removePaths(p, &d->files, &d->directories);
408 if (d->poller)
409 p = d->poller->removePaths(p, &d->files, &d->directories);
410
411 return p;
412}
413
459{
460 Q_D(const QFileSystemWatcher);
461 return d->directories;
462}
463
465{
466 Q_D(const QFileSystemWatcher);
467 return d->files;
468}
469
471
472#include "moc_qfilesystemwatcher.cpp"
473#include "moc_qfilesystemwatcher_p.cpp"
474
QFileSystemWatcherEngine * native
void _q_fileChanged(const QString &path, bool removed)
QFileSystemWatcherEngine * poller
void _q_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)
\inmodule QtCore
Definition qobject.h:90
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2823
QString objectName
the name of this object
Definition qobject.h:94
iterator end()
Definition qset.h:140
iterator find(const T &value)
Definition qset.h:159
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:76
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
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:5299
QString mid(qsizetype position, qsizetype n=-1) const
Returns a string that contains n characters of this string, starting at the specified position index.
Definition qstring.cpp:5204
void driveLockForRemoval(const QString &)
void driveLockForRemovalFailed(const QString &)
void driveRemoved(const QString &)
QSet< QString >::iterator it
Combined button and popup list for selecting options.
@ CaseInsensitive
#define Q_UNLIKELY(x)
static QStringList empty_paths_pruned(const QStringList &paths)
#define qWarning
Definition qlogging.h:162
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
#define SLOT(a)
Definition qobjectdefs.h:51
#define SIGNAL(a)
Definition qobjectdefs.h:52
GLfloat GLfloat f
GLsizei const GLuint * paths
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLsizei const GLchar *const * path
GLdouble s
[6]
Definition qopenglext.h:235
GLfloat GLfloat p
[1]
#define emit
#define Q_UNUSED(x)
QObject::connect nullptr
QJSEngine engine
[0]
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent