5 #if !defined(__EMSCRIPTEN__)
7 public class FileChange
10 bool created:1; // param: new file name
11 bool renamed:1; // param: new file name
14 bool attribs:1; // param: new attributes
17 public define AnyFileChange = FileChange { true, true, true, true, true };
19 // *** File Monitoring ***
21 static class FileEntry : struct
35 public class FileMonitor
38 property void * userData { set { data = value; } };
39 property FileChange fileChange { set { watchFor = value; } };
40 property const char * fileName
47 if(value) MonitorFile(value);
50 get { return fileName; }
52 property const char * directoryName
59 if(value) MonitorDirectory(value);
62 get { return fileName; }
69 globalSystem.fileMonitorMutex.Wait();
74 fileNotifies.Free(FileNotify::Free);
80 globalSystem.fileMonitorMutex.Release();
84 void StartMonitoring()
86 if(fileName && !active)
88 globalSystem.fileMonitorMutex.Wait();
97 MonitorDirectory(fileName);
99 MonitorFile(fileName);
101 globalSystem.fileMonitorMutex.Release();
106 FileMonitor prev, next;
110 bool reentrant, toBeFreed;
111 OldList fileNotifies;
119 public virtual bool any_object::OnDirNotify(FileChange action, const char * fileName, const char * param);
121 // For directory monitors
124 public virtual bool any_object::OnFileNotify(FileChange action, const char * param);
127 #if defined(__WIN32__)
129 #elif defined(__linux__)
136 globalSystem.fileMonitorMutex.Wait();
141 globalSystem.fileMonitorMutex.Release();
144 void MonitorFile(const char * filePath)
146 FileAttribs exists = FileExists(filePath);
148 String oldFileName = fileName;
151 globalSystem.fileMonitorMutex.Wait();
153 files.Free(FileEntry::Free);
154 fileNotifies.Free(FileNotify::Free);
156 if(!active && !toBeFreed)
157 globalSystem.fileMonitors.Add(this);
158 this.exists = exists && !exists.isDirectory;
159 fileName = CopyString(filePath);
161 this.watchFor = watchFor;
163 if(FileGetStats(filePath, stats))
165 attribs = stats.attribs;
167 modified = stats.modified;
169 /*if(strstr(filePath, "Debugger"))
170 printf("modified is %x", modified);*/
177 globalSystem.fileMonitorMutex.Release();
179 if(!globalSystem.fileMonitorThread)
181 globalSystem.fileMonitorThread = MonitorThread { };
182 //incref globalSystem.fileMonitorThread;
183 globalSystem.fileMonitorThread.Create();
187 void MonitorDirectory(const char * filePath)
190 String oldFileName = fileName;
193 globalSystem.fileMonitorMutex.Wait();
195 files.Free(FileEntry::Free);
196 fileNotifies.Free(FileNotify::Free);
198 if(!active && !toBeFreed)
199 globalSystem.fileMonitors.Add(this);
201 exists = FileExists(filePath).isDirectory;
203 this.watchFor = watchFor;
204 fileName = CopyString(filePath);
207 if(FileGetStats(fileName, stats))
209 attribs = stats.attribs;
211 modified = stats.modified;
214 // Initialize files in directory
215 GetFileEntries(&files, fileName);
220 globalSystem.fileMonitorMutex.Release();
222 if(!globalSystem.fileMonitorThread)
224 globalSystem.fileMonitorThread = MonitorThread { };
225 globalSystem.fileMonitorThread.Create();
231 files.Free(FileEntry::Free);
232 fileNotifies.Free(FileNotify::Free);
234 globalSystem.fileMonitors.Remove(this);
238 bool AddFileNotify(FileChange action, const char * fileName, const char * param)
240 if(watchFor & action)
242 fileNotifies.Add(FileNotify {
243 monitor = this, action = action, fileName = CopyString(fileName), param = CopyString(param)
251 private class FileNotify : struct
253 FileNotify prev, next;
266 static define FILE_MONITOR_RESOLUTION = 1.0; // Once every second
268 static int CompareFiles(FileEntry e1, FileEntry e2, void * data)
270 if(!e1.attribs.isDirectory && e2.attribs.isDirectory)
272 else if(e1.attribs.isDirectory && !e2.attribs.isDirectory)
275 return strcmp(e1.name, e2.name);
278 static void GetFileEntries(OldList list, const char * path)
280 FileListing listing { path, null };
281 while(listing.Find())
284 name = CopyString(listing.name),
285 attribs = listing.stats.attribs,
286 modified = listing.stats.modified,
287 size = listing.stats.size
290 list.Sort(CompareFiles, null);
293 static class MonitorThread : Thread
299 while(!globalSystem.systemTerminate)
301 Time currentTime = GetTime();
302 if(currentTime - lastTime > FILE_MONITOR_RESOLUTION)
305 bool fileActivity = false;
306 lastTime = currentTime;
308 // printf("[%d] Waiting in MonitorThread for fileMonitor Mutex %x...\n", (int)GetCurrentThreadID(), globalSystem.fileMonitorMutex);
309 globalSystem.fileMonitorMutex.Wait();
311 for(monitor = globalSystem.fileMonitors.first; monitor; monitor = monitor.next)
313 if(monitor.directory)
315 bool exists = FileExists(monitor.fileName).isDirectory;
316 if(exists && !monitor.exists)
318 fileActivity |= monitor.AddFileNotify(FileChange { created = true }, monitor.fileName, null);
319 monitor.exists = exists;
321 else if(!exists && monitor.exists)
323 fileActivity |= monitor.AddFileNotify(FileChange { deleted = true }, monitor.fileName, null);
324 monitor.exists = exists;
326 else if(exists && monitor.watchFor & FileChange { created = true, deleted = true, modified = true, attribs = true})
330 OldList newEntries = { 0 };
331 GetFileEntries(&newEntries, monitor.fileName);
333 // Directory comparison
334 entry = monitor.files.first;
335 newEntry = newEntries.first;
337 while(entry || newEntry)
340 if(!entry) comparison = 1;
341 else if(!newEntry) comparison = -1;
342 else comparison = strcmp(entry.name,newEntry.name);
346 fileActivity |= monitor.AddFileNotify(FileChange { created = true }, newEntry.name, null);
347 newEntry = newEntry.next;
349 else if(comparison < 0)
351 fileActivity |= monitor.AddFileNotify(FileChange { deleted = true }, entry.name, null);
356 if(newEntry.modified != entry.modified || newEntry.size != entry.size)
358 fileActivity |= monitor.AddFileNotify(FileChange { modified = true }, entry.name, null);
360 else if(newEntry.attribs != entry.attribs)
362 fileActivity |= monitor.AddFileNotify(FileChange { attribs = true }, entry.name, null);
365 newEntry = newEntry.next;
368 monitor.files.Free(FileEntry::Free);
369 monitor.files = newEntries;
372 else if(monitor.fileName)
374 FileAttribs exists = FileExists(monitor.fileName);
375 // printf("Iterating on Monitor for %s...\n", monitor.fileName);
377 if((exists && !exists.isDirectory) && !monitor.exists)
379 fileActivity |= monitor.AddFileNotify(FileChange { created = true }, monitor.fileName, null);
380 monitor.exists = true;
382 else if((!exists || exists.isDirectory) && monitor.exists)
384 fileActivity |= monitor.AddFileNotify(FileChange { deleted = true }, monitor.fileName, null);
385 monitor.exists = false;
387 else if(monitor.exists)
390 if(FileGetStats(monitor.fileName, stats))
393 /*if(strstr(monitor.fileName, "Debugger"))
394 printf("Now modified is %x\n", stats.modified);*/
397 if(stats.modified != monitor.modified || stats.size != monitor.size)
399 // printf("Modified/size changed, adding file notify...\n");
400 if(stats.modified > monitor.modified || monitor.modified - (TimeStamp)stats.modified > 2)
401 fileActivity |= monitor.AddFileNotify(FileChange { modified = true }, monitor.fileName, null);
402 monitor.size = stats.size;
403 monitor.modified = stats.modified;
405 /*if(strstr(monitor.fileName, "Debugger"))
406 printf("Setting modified to %x\n", monitor.modified);*/
409 else if(stats.attribs != monitor.attribs)
411 // printf("Attribs changed, adding file notify...\n");
413 fileActivity |= monitor.AddFileNotify(FileChange { attribs = true }, monitor.fileName, null);
414 monitor.attribs = stats.attribs;
422 // printf("[%d] Signaling Event...\n", (int)GetCurrentThreadID());
423 guiApp.SignalEvent();
426 // printf("[%d] Releasing Mutex...\n", (int)GetCurrentThreadID());
427 globalSystem.fileMonitorMutex.Release();
430 // printf("[%d] Sleeping...\n", (int)GetCurrentThreadID());
437 #endif // !defined(__EMSCRIPTEN__)