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
4#include "qfilesystemwatcher_polling_p.h"
5
6#include <QtCore/qlatin1stringview.h>
7#include <QtCore/qscopeguard.h>
8#include <QtCore/qtimer.h>
9
10#include <chrono>
11
12using namespace std::chrono_literals;
13
14QT_BEGIN_NAMESPACE
15
16using namespace Qt::StringLiterals;
17
18static constexpr auto PollingInterval = 1s;
19
20QPollingFileSystemWatcherEngine::QPollingFileSystemWatcherEngine(QObject *parent)
21 : QFileSystemWatcherEngine(parent)
22{
23}
24
25QStringList QPollingFileSystemWatcherEngine::addPaths(const QStringList &paths,
26 QStringList *files,
27 QStringList *directories)
28{
29 QStringList unhandled;
30 for (const QString &path : paths) {
31 auto sg = qScopeGuard(f: [&]{ unhandled.push_back(t: path); });
32 QFileInfo fi(path);
33 if (!fi.exists())
34 continue;
35 if (fi.isDir()) {
36 if (directories->contains(str: path))
37 continue;
38 directories->append(t: path);
39 if (!path.endsWith(c: u'/'))
40 fi = QFileInfo(path + u'/');
41 this->directories.insert(key: path, value: fi);
42 } else {
43 if (files->contains(str: path))
44 continue;
45 files->append(t: path);
46 this->files.insert(key: path, value: fi);
47 }
48 sg.dismiss();
49 }
50
51 std::chrono::milliseconds interval = PollingInterval;
52#ifdef QT_BUILD_INTERNAL
53 if (Q_UNLIKELY(parent()->objectName().startsWith("_qt_autotest_force_engine_"_L1))) {
54 interval = 10ms; // Special case to speed up the unittests
55 }
56#endif
57
58 if ((!this->files.isEmpty() ||
59 !this->directories.isEmpty()) &&
60 !timer.isActive()) {
61 timer.start(duration: interval, obj: this);
62 }
63
64 return unhandled;
65}
66
67QStringList QPollingFileSystemWatcherEngine::removePaths(const QStringList &paths,
68 QStringList *files,
69 QStringList *directories)
70{
71 QStringList unhandled;
72 for (const QString &path : paths) {
73 if (this->directories.remove(key: path)) {
74 directories->removeAll(t: path);
75 } else if (this->files.remove(key: path)) {
76 files->removeAll(t: path);
77 } else {
78 unhandled.push_back(t: path);
79 }
80 }
81
82 if (this->files.isEmpty() &&
83 this->directories.isEmpty()) {
84 timer.stop();
85 }
86
87 return unhandled;
88}
89
90void QPollingFileSystemWatcherEngine::timerEvent(QTimerEvent *e)
91{
92 if (e->timerId() != timer.timerId())
93 return QFileSystemWatcherEngine::timerEvent(event: e);
94
95 for (auto it = files.begin(), end = files.end(); it != end; /*erasing*/) {
96 QString path = it.key();
97 QFileInfo fi(path);
98 if (!fi.exists()) {
99 it = files.erase(it);
100 emit fileChanged(path, removed: true);
101 continue;
102 } else if (it.value() != fi) {
103 it.value() = fi;
104 emit fileChanged(path, removed: false);
105 }
106 ++it;
107 }
108
109 for (auto it = directories.begin(), end = directories.end(); it != end; /*erasing*/) {
110 QString path = it.key();
111 QFileInfo fi(path);
112 if (!path.endsWith(c: u'/'))
113 fi = QFileInfo(path + u'/');
114 if (!fi.exists()) {
115 it = directories.erase(it);
116 emit directoryChanged(path, removed: true);
117 continue;
118 } else if (it.value() != fi) {
119 fi.refresh();
120 if (!fi.exists()) {
121 it = directories.erase(it);
122 emit directoryChanged(path, removed: true);
123 continue;
124 } else {
125 it.value() = fi;
126 emit directoryChanged(path, removed: false);
127 }
128 }
129 ++it;
130 }
131}
132
133QT_END_NAMESPACE
134
135#include "moc_qfilesystemwatcher_polling_p.cpp"
136

source code of qtbase/src/corelib/io/qfilesystemwatcher_polling.cpp