Mercurial > minori
annotate src/core/filesystem.cc @ 399:a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
| author | Paper <paper@tflc.us> |
|---|---|
| date | Fri, 07 Nov 2025 15:28:22 -0500 |
| parents | 650a9159a0e7 |
| children | 2f89797b6a44 |
| rev | line source |
|---|---|
| 36 | 1 #include "core/filesystem.h" |
| 15 | 2 #include "core/config.h" |
| 62 | 3 #include "core/strings.h" |
| 250 | 4 |
| 5 #include <QStandardPaths> | |
| 6 | |
|
135
0a458cb26ff4
filesystem: move to using std::filesystem after C++17 switch
Paper <mrpapersonic@gmail.com>
parents:
134
diff
changeset
|
7 #include <filesystem> |
| 2 | 8 |
| 398 | 9 #ifdef WIN32 |
| 10 # include <windows.h> | |
| 11 #endif | |
| 12 | |
| 11 | 13 namespace Filesystem { |
| 14 | |
|
135
0a458cb26ff4
filesystem: move to using std::filesystem after C++17 switch
Paper <mrpapersonic@gmail.com>
parents:
134
diff
changeset
|
15 /* this runs fs::create_directories() on the |
|
0a458cb26ff4
filesystem: move to using std::filesystem after C++17 switch
Paper <mrpapersonic@gmail.com>
parents:
134
diff
changeset
|
16 PARENT directory. */ |
| 369 | 17 void CreateDirectories(const std::filesystem::path &path) |
| 18 { | |
|
158
80d6b28eb29f
dep/animia: fix most X11 stuff
Paper <mrpapersonic@gmail.com>
parents:
138
diff
changeset
|
19 if (path.empty()) |
|
80d6b28eb29f
dep/animia: fix most X11 stuff
Paper <mrpapersonic@gmail.com>
parents:
138
diff
changeset
|
20 return; |
|
80d6b28eb29f
dep/animia: fix most X11 stuff
Paper <mrpapersonic@gmail.com>
parents:
138
diff
changeset
|
21 |
| 369 | 22 const auto &parent = path.parent_path(); |
|
158
80d6b28eb29f
dep/animia: fix most X11 stuff
Paper <mrpapersonic@gmail.com>
parents:
138
diff
changeset
|
23 if (!std::filesystem::exists(parent)) |
|
80d6b28eb29f
dep/animia: fix most X11 stuff
Paper <mrpapersonic@gmail.com>
parents:
138
diff
changeset
|
24 std::filesystem::create_directories(parent); |
| 11 | 25 } |
| 26 | |
| 369 | 27 std::filesystem::path GetDotPath() |
| 28 { | |
| 250 | 29 /* |
| 30 * Windows: ~/AppData/Roaming/Minori | |
| 31 * macOS: ~/Library/Application Support/Minori | |
| 32 * ...: ~/.config/minori | |
| 33 * | |
| 34 * FIXME: are windows and mac properly cased? | |
| 258 | 35 */ |
| 9 | 36 #ifdef WIN32 |
| 250 | 37 return Strings::ToUtf8String(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)); |
| 38 #else | |
| 39 return Strings::ToUtf8String(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation)); | |
| 40 #endif | |
|
45
4b05bc7668eb
filesystem: split config path into dotpath and config, add anime db path
Paper <mrpapersonic@gmail.com>
parents:
44
diff
changeset
|
41 } |
|
4b05bc7668eb
filesystem: split config path into dotpath and config, add anime db path
Paper <mrpapersonic@gmail.com>
parents:
44
diff
changeset
|
42 |
| 369 | 43 std::filesystem::path GetConfigPath() |
| 44 { | |
|
135
0a458cb26ff4
filesystem: move to using std::filesystem after C++17 switch
Paper <mrpapersonic@gmail.com>
parents:
134
diff
changeset
|
45 return GetDotPath() / CONFIG_NAME; |
|
118
39521c47c7a3
*: another huge megacommit, SORRY
Paper <mrpapersonic@gmail.com>
parents:
106
diff
changeset
|
46 } |
|
39521c47c7a3
*: another huge megacommit, SORRY
Paper <mrpapersonic@gmail.com>
parents:
106
diff
changeset
|
47 |
| 369 | 48 std::filesystem::path GetAnimeDBPath() |
| 49 { | |
|
135
0a458cb26ff4
filesystem: move to using std::filesystem after C++17 switch
Paper <mrpapersonic@gmail.com>
parents:
134
diff
changeset
|
50 return GetDotPath() / "anime" / "db.json"; |
|
45
4b05bc7668eb
filesystem: split config path into dotpath and config, add anime db path
Paper <mrpapersonic@gmail.com>
parents:
44
diff
changeset
|
51 } |
|
4b05bc7668eb
filesystem: split config path into dotpath and config, add anime db path
Paper <mrpapersonic@gmail.com>
parents:
44
diff
changeset
|
52 |
| 369 | 53 std::filesystem::path GetTorrentsPath() |
| 54 { | |
|
230
2f5a9247e501
torrents: implement download button
Paper <paper@paper.us.eu.org>
parents:
221
diff
changeset
|
55 return GetDotPath() / "torrents"; |
|
2f5a9247e501
torrents: implement download button
Paper <paper@paper.us.eu.org>
parents:
221
diff
changeset
|
56 } |
|
2f5a9247e501
torrents: implement download button
Paper <paper@paper.us.eu.org>
parents:
221
diff
changeset
|
57 |
| 378 | 58 std::filesystem::path GetAnimePostersPath() |
| 59 { | |
| 60 return GetDotPath() / "anime" / "posters"; | |
| 61 } | |
| 62 | |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
63 /* ------------------------------------------------------------------------ */ |
|
399
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
64 /* This is the "base class" for basically every watcher. */ |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
65 |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
66 class Watcher : public IWatcher { |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
67 public: |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
68 Watcher(void *opaque, const std::filesystem::path &path, EventHandler handler) |
| 393 | 69 : path_(path), handler_(handler), opaque_(opaque) |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
70 { |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
71 } |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
72 |
| 393 | 73 virtual ~Watcher() override {} |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
74 |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
75 protected: |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
76 std::filesystem::path path_; |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
77 EventHandler handler_; |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
78 void *opaque_; |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
79 }; |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
80 |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
81 /* ------------------------------------------------------------------------ */ |
|
391
c3f717b7321b
filesystem: only iterate over the list once when erasing deleted files
Paper <paper@tflc.us>
parents:
382
diff
changeset
|
82 /* Filesystem watcher. |
|
399
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
83 * This is the portable version for non-Windows; however, pretty much |
|
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
84 * every implementation should be based on this in some shape or form. */ |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
85 |
| 398 | 86 /* |
| 87 We can't use std::recursive_directory_iterator! | |
| 88 Why? Well, to put it blunt, it sucks. The main reason | |
| 89 being is that if it finds a single directory it cannot | |
| 90 recurse into, instead of safely recovering, it just | |
| 91 completely stops. I am dumbfounded at this behavior, but | |
| 92 nevertheless it means we simply can't use it, and must | |
| 93 resort to old-fashioned recursion. --paper | |
| 94 */ | |
| 95 static void IterateDirectory(const std::filesystem::path &path, bool recursive, | |
| 96 std::function<void(const std::filesystem::path &path)> func) | |
| 97 { | |
| 98 std::error_code ec; | |
| 99 static const std::filesystem::directory_iterator end; | |
| 100 | |
| 101 for (std::filesystem::directory_iterator item(path, ec); item != end; item.increment(ec)) { | |
| 102 if (ec) | |
| 103 continue; | |
| 104 | |
| 105 std::filesystem::path p = item->path(); | |
| 106 | |
| 107 /* Hand the path off to the listener */ | |
| 108 func(p); | |
| 109 | |
| 110 /* If we're dealing with another directory, recurse into it. */ | |
| 111 if (recursive && item->is_directory()) | |
| 112 IterateDirectory(p, true, func); | |
| 113 } | |
| 114 } | |
| 115 | |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
116 class StdFilesystemWatcher : public Watcher { |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
117 public: |
| 398 | 118 StdFilesystemWatcher(void *opaque, const std::filesystem::path &path, EventHandler handler, bool recursive) |
| 119 : Watcher(opaque, path, handler), recursive_(recursive) | |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
120 { |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
121 } |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
122 |
| 393 | 123 virtual ~StdFilesystemWatcher() override {} |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
124 |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
125 virtual void Process() override |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
126 { |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
127 /* Untoggle all paths. This allows us to only ever |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
128 * iterate over the directory ONCE. */ |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
129 UntoggleAllPaths(); |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
130 |
| 398 | 131 /* Start iterating directories. If we're recursive, this |
| 132 * will go through the whole tree. */ | |
| 133 IterateDirectory(path_, recursive_, [this](const std::filesystem::path &p) { | |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
134 if (FindAndTogglePath(p)) |
| 398 | 135 return; |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
136 |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
137 handler_(opaque_, p, Event::Created); |
| 398 | 138 paths_.insert({p, true}); |
| 139 }); | |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
140 |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
141 DeleteUntoggledPaths(); |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
142 } |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
143 |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
144 protected: |
|
391
c3f717b7321b
filesystem: only iterate over the list once when erasing deleted files
Paper <paper@tflc.us>
parents:
382
diff
changeset
|
145 bool FindAndTogglePath(const std::filesystem::path &p) |
|
c3f717b7321b
filesystem: only iterate over the list once when erasing deleted files
Paper <paper@tflc.us>
parents:
382
diff
changeset
|
146 { |
| 398 | 147 auto it = paths_.find(p); |
| 148 if (it == paths_.end()) | |
| 149 return false; | |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
150 |
| 398 | 151 it->second = true; |
| 152 return true; | |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
153 } |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
154 |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
155 void UntoggleAllPaths() |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
156 { |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
157 for (auto &pp : paths_) |
| 398 | 158 pp.second = false; |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
159 } |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
160 |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
161 void DeleteUntoggledPaths() |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
162 { |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
163 auto it = paths_.begin(); |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
164 |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
165 while (it != paths_.end()) { |
| 398 | 166 if (!it->second) { |
| 167 handler_(opaque_, it->first, Event::Deleted); | |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
168 it = paths_.erase(it); |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
169 } else { |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
170 it++; |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
171 } |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
172 } |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
173 } |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
174 |
| 398 | 175 /* unordered hashmap, path[found] */ |
| 176 std::unordered_map<std::filesystem::path, bool> paths_; | |
| 177 bool recursive_; | |
| 178 }; | |
| 179 | |
|
399
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
180 /* There does actually exist an API in Qt to do file system watching. |
|
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
181 * However, it is of little use for us, since it |
|
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
182 * a) isn't recursive |
|
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
183 * b) requires constant conversion to and from QString, which takes |
|
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
184 * up valuable system resources |
|
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
185 * c) uses signals etc. that are a pain in the ass to use in an |
|
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
186 * event-processing logic like we do here. */ |
|
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
187 |
| 398 | 188 #ifdef WIN32 |
|
399
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
189 /* On Windows, we can ask the OS whether the directory tree has changed or not. |
| 398 | 190 * This is great for us! */ |
| 191 class Win32Watcher : public StdFilesystemWatcher { | |
| 192 public: | |
| 193 Win32Watcher(void *opaque, const std::filesystem::path &path, IWatcher::EventHandler handler, bool recursive) | |
| 194 : StdFilesystemWatcher(opaque, path, handler, recursive), dirwatcher_(INVALID_HANDLE_VALUE), first_(true) | |
| 195 { | |
| 196 } | |
| 197 | |
| 198 virtual ~Win32Watcher() override | |
| 199 { | |
| 200 // delete handle | |
| 201 if (dirwatcher_ != INVALID_HANDLE_VALUE) | |
| 202 FindCloseChangeNotification(dirwatcher_); | |
| 203 } | |
| 204 | |
| 205 virtual void Process() override | |
| 206 { | |
| 207 if (first_ || dirwatcher_ == INVALID_HANDLE_VALUE) { | |
| 208 /* We want to create this right before iteration so | |
| 209 * we minimize possible race conditions while also | |
| 210 * reducing unnecessary processing */ | |
| 211 TryCreateDirWatcher(); | |
| 212 StdFilesystemWatcher::Process(); | |
| 213 first_ = false; | |
| 214 return; | |
| 215 } | |
| 216 | |
| 217 /* We have a valid handle */ | |
| 218 if (WaitForSingleObject(dirwatcher_, 0) != WAIT_OBJECT_0) | |
| 219 return; | |
| 220 | |
| 221 StdFilesystemWatcher::Process(); | |
| 222 FindNextChangeNotification(dirwatcher_); | |
| 223 } | |
| 224 | |
| 225 protected: | |
| 226 bool TryCreateDirWatcher() | |
| 227 { | |
| 228 dirwatcher_ = FindFirstChangeNotificationW(Watcher::path_.wstring().c_str(), recursive_, | |
| 229 FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME); | |
| 230 | |
| 231 return (dirwatcher_ != INVALID_HANDLE_VALUE); | |
| 232 } | |
| 233 | |
| 234 /* variables */ | |
| 235 HANDLE dirwatcher_; | |
| 236 bool first_; | |
| 237 }; | |
| 238 | |
|
399
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
239 /* I think "Vista" is actually a misnomer, MSDN says that ReadDirectoryChangesW |
|
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
240 * has existed since Windows XP. |
|
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
241 * |
|
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
242 * Anyway, this is a version that doesn't need to keep the whole directory |
|
a0bc3ae5164a
anime_list: actually obey "highlight anime if they are available"
Paper <paper@tflc.us>
parents:
398
diff
changeset
|
243 * tree in-memory if it is available. */ |
| 398 | 244 class Win32WatcherVista : public Win32Watcher { |
| 245 public: | |
| 246 Win32WatcherVista(void *opaque, const std::filesystem::path &path, IWatcher::EventHandler handler, bool recursive) | |
| 247 : Win32Watcher(opaque, path, handler, recursive), dirhandle_(INVALID_HANDLE_VALUE) | |
| 248 { | |
| 249 overlapped_.hEvent = nullptr; | |
| 250 } | |
| 251 | |
| 252 virtual ~Win32WatcherVista() override | |
| 253 { | |
| 254 if (dirhandle_ != INVALID_HANDLE_VALUE) | |
| 255 CloseHandle(dirhandle_); | |
| 256 | |
| 257 if (overlapped_.hEvent != nullptr) | |
| 258 CloseHandle(overlapped_.hEvent); | |
| 259 } | |
| 260 | |
| 261 virtual void Process() override | |
| 262 { | |
| 263 if (first_) { | |
| 264 if (TryCreateHandles()) { | |
| 265 /* Queue a directory read asynchronously. On the next run, this will | |
| 266 * be waited on. */ | |
| 267 QueueDirectoryRead(); | |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
268 |
| 398 | 269 /* Avoid running Win32Watcher::Process, as it will read the whole |
| 270 * directory tree into memory. Instead, iterate through the directory | |
| 271 * ourselves. */ | |
| 272 IterateDirectory(path_, recursive_, | |
| 273 [this](const std::filesystem::path &p) { handler_(opaque_, p, Event::Created); }); | |
| 274 } else { | |
| 275 /* Uh oh; we might have to fall back to Win32Watcher. Call into it to | |
| 276 * load the tree into memory. */ | |
| 277 Win32Watcher::Process(); | |
| 278 } | |
| 279 | |
| 280 first_ = false; | |
| 281 return; | |
| 282 } | |
| 283 | |
| 284 if (!HandlesAreValid()) { | |
| 285 /* If we're here, we already fell back into Win32Watcher, so just | |
| 286 * call back into again. On the slim chance TryCreateHandles() might | |
| 287 * succeed, call it as well... */ | |
| 288 if (TryCreateHandles()) | |
| 289 QueueDirectoryRead(); | |
| 290 Win32Watcher::Process(); | |
| 291 return; | |
| 292 } | |
| 293 | |
| 294 if (WaitForSingleObject(overlapped_.hEvent, 0) != WAIT_OBJECT_0) | |
| 295 return; | |
| 296 | |
| 297 FILE_NOTIFY_INFORMATION *ev = reinterpret_cast<FILE_NOTIFY_INFORMATION *>(change_buf_); | |
| 298 | |
| 299 for (;;) { | |
| 300 /* Tack on the file name to the end of our path */ | |
| 301 std::filesystem::path p = Watcher::path_ / std::wstring(ev->FileName, ev->FileNameLength / sizeof(WCHAR)); | |
| 302 | |
| 303 switch (ev->Action) { | |
| 304 case FILE_ACTION_ADDED: | |
| 305 case FILE_ACTION_RENAMED_NEW_NAME: | |
| 306 /* File was added */ | |
| 307 handler_(opaque_, p, Event::Created); | |
| 308 break; | |
| 309 case FILE_ACTION_REMOVED: | |
| 310 case FILE_ACTION_RENAMED_OLD_NAME: | |
| 311 /* File was removed */ | |
| 312 handler_(opaque_, p, Event::Deleted); | |
| 313 break; | |
| 314 } | |
| 315 | |
| 316 if (ev->NextEntryOffset) { | |
| 317 /* ugh ugh ugh ugh */ | |
| 318 ev = reinterpret_cast<FILE_NOTIFY_INFORMATION *>(reinterpret_cast<char *>(ev) + ev->NextEntryOffset); | |
| 319 } else { | |
| 320 break; | |
| 321 } | |
| 322 } | |
| 323 | |
| 324 /* Queue a directory read for the next call */ | |
| 325 QueueDirectoryRead(); | |
| 326 } | |
| 327 | |
| 328 protected: | |
| 329 bool HandlesAreValid() { return (overlapped_.hEvent && dirhandle_ != INVALID_HANDLE_VALUE); } | |
| 330 | |
| 331 bool TryCreateHandles() | |
| 332 { | |
| 333 if (!overlapped_.hEvent) { | |
| 334 overlapped_.hEvent = CreateEventW(nullptr, FALSE, 0, nullptr); | |
| 335 if (!overlapped_.hEvent) | |
| 336 return false; | |
| 337 } | |
| 338 | |
| 339 if (dirhandle_ == INVALID_HANDLE_VALUE) { | |
| 340 dirhandle_ = | |
| 341 CreateFileW(Watcher::path_.wstring().c_str(), FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE, | |
| 342 nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED, nullptr); | |
| 343 if (dirhandle_ == INVALID_HANDLE_VALUE) | |
| 344 return false; | |
| 345 } | |
| 346 | |
| 347 /* We're done here */ | |
| 348 return true; | |
| 349 } | |
| 350 | |
| 351 bool QueueDirectoryRead() | |
| 352 { | |
| 353 return ReadDirectoryChangesW(dirhandle_, change_buf_, sizeof(change_buf_), TRUE, | |
| 354 FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_DIR_NAME, nullptr, &overlapped_, | |
| 355 nullptr); | |
| 356 } | |
| 357 | |
| 358 HANDLE dirhandle_; | |
| 359 OVERLAPPED overlapped_; | |
| 360 alignas(FILE_NOTIFY_INFORMATION) char change_buf_[4096]; | |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
361 }; |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
362 |
| 398 | 363 using DefaultWatcher = Win32WatcherVista; |
| 364 #else | |
| 365 using DefaultWatcher = StdFilesystemWatcher; | |
| 366 #endif | |
| 367 | |
| 393 | 368 IWatcher *GetRecursiveFilesystemWatcher(void *opaque, const std::filesystem::path &path, IWatcher::EventHandler handler) |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
369 { |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
370 /* .... :) */ |
| 398 | 371 return new DefaultWatcher(opaque, path, handler, true); |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
372 } |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
373 |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
374 IWatcher *GetFilesystemWatcher(void *opaque, const std::filesystem::path &path, IWatcher::EventHandler handler) |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
375 { |
| 398 | 376 return new DefaultWatcher(opaque, path, handler, false); |
|
382
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
377 } |
|
0265e125f680
filesystem: implement filesystem watcher
Paper <paper@tflc.us>
parents:
378
diff
changeset
|
378 |
| 15 | 379 } // namespace Filesystem |
