Browse Source

Applied Linux FileWatcher patch from Alex Fuller.

Lasse Öörni 13 years ago
parent
commit
cee4fa709a
2 changed files with 177 additions and 60 deletions
  1. 157 51
      Engine/IO/FileWatcher.cpp
  2. 20 9
      Engine/IO/FileWatcher.h

+ 157 - 51
Engine/IO/FileWatcher.cpp

@@ -25,12 +25,18 @@
 #include "FileSystem.h"
 #include "FileSystem.h"
 #include "FileWatcher.h"
 #include "FileWatcher.h"
 #include "Log.h"
 #include "Log.h"
-
-#ifdef WIN32
-#include <windows.h>
-#endif
-
-namespace Urho3D
+
+#ifdef WIN32
+#include <windows.h>
+#elif __linux__
+#include <sys/inotify.h>
+extern "C" {
+// Need read/close for inotify
+#include "unistd.h"
+}
+#endif
+
+namespace Urho3D
 {
 {
 
 
 static const unsigned BUFFERSIZE = 4096;
 static const unsigned BUFFERSIZE = 4096;
@@ -39,17 +45,27 @@ OBJECTTYPESTATIC(FileWatcher);
 
 
 FileWatcher::FileWatcher(Context* context) :
 FileWatcher::FileWatcher(Context* context) :
     Object(context),
     Object(context),
-    fileSystem_(GetSubsystem<FileSystem>()),
-    watchSubDirs_(false)
-{
-}
-
-FileWatcher::~FileWatcher()
-{
-    StopWatching();
-}
-
-bool FileWatcher::StartWatching(const String& pathName, bool watchSubDirs)
+    fileSystem_(GetSubsystem<FileSystem>()),
+    watchSubDirs_(false)
+{
+#if defined(ENABLE_FILEWATCHER)
+#if defined(__linux__)
+    watchHandle_ = inotify_init();
+#endif
+#endif
+}
+
+FileWatcher::~FileWatcher()
+{
+    StopWatching();
+#if defined(ENABLE_FILEWATCHER)
+#if defined(__linux__)
+    close(watchHandle_);
+#endif
+#endif
+}
+
+bool FileWatcher::StartWatching(const String& pathName, bool watchSubDirs)
 {
 {
     if (!fileSystem_)
     if (!fileSystem_)
     {
     {
@@ -57,13 +73,14 @@ bool FileWatcher::StartWatching(const String& pathName, bool watchSubDirs)
         return false;
         return false;
     }
     }
     
     
-    // Stop any previous watching
-    StopWatching();
-    
-#if defined(WIN32) && defined(ENABLE_FILEWATCHER)
-    String nativePath = GetNativePath(RemoveTrailingSlash(pathName));
-    
-    dirHandle_ = (void*)CreateFileW(
+    // Stop any previous watching
+    StopWatching();
+    
+#if defined(ENABLE_FILEWATCHER)
+#if defined(WIN32)
+    String nativePath = GetNativePath(RemoveTrailingSlash(pathName));
+    
+    dirHandle_ = (void*)CreateFileW(
         WString(nativePath).CString(),
         WString(nativePath).CString(),
         FILE_LIST_DIRECTORY,
         FILE_LIST_DIRECTORY,
         FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
         FILE_SHARE_WRITE | FILE_SHARE_READ | FILE_SHARE_DELETE,
@@ -83,12 +100,61 @@ bool FileWatcher::StartWatching(const String& pathName, bool watchSubDirs)
     }
     }
     else
     else
     {
     {
-        LOGERROR("Failed to start watching path " + pathName);
-        return false;
-    }
-#else
-    /// \todo Implement on Unix-like systems
-    LOGERROR("FileWatcher not implemented, can not start watching path " + pathName);
+        LOGERROR("Failed to start watching path " + pathName);
+        return false;
+    }
+#elif defined(__linux__)
+    //String nativePath = GetNativePath(RemoveTrailingSlash(pathName));
+
+    int flags = IN_CREATE|IN_DELETE|IN_MODIFY|IN_MOVED_FROM|IN_MOVED_TO;
+    int handle;
+    dirHandle_;
+    handle = inotify_add_watch(watchHandle_, pathName.CString(), flags);
+
+    if (handle < 0)
+    {
+        LOGERROR("Failed to start watching path " + pathName);
+        return false;
+    }
+    else
+    {
+        // Store the root path here when reconstructed with inotify later
+        dirHandle_[handle] = "";
+        path_ = AddTrailingSlash(pathName);
+        watchSubDirs_ = watchSubDirs;
+
+        if (watchSubDirs_)
+        {
+            Vector<String> subDirs;
+            fileSystem_->ScanDir(subDirs, pathName, "*", SCAN_DIRS, true);
+
+            for (unsigned i = 0; i < subDirs.Size(); ++i)
+            {
+                String subDirFullPath = AddTrailingSlash(path_ + subDirs[i]);
+
+                // Don't watch ./ or ../ sub-directories
+                if (!subDirFullPath.EndsWith("./"))
+                {
+                    handle = inotify_add_watch(watchHandle_, subDirFullPath.CString(), flags);
+                    if (handle < 0)
+                        LOGERROR("Failed to start watching subdirectory path " + subDirFullPath);
+                    else
+                    {
+                        // Store sub-directory to reconstruct later from inotify
+                        dirHandle_[handle] = AddTrailingSlash(subDirs[i]);
+                    }
+                }
+            }
+        }
+        Start();
+
+        LOGDEBUG("Started watching path " + pathName);
+        return true;
+    }
+#endif
+#else
+    /// \todo Implement on Unix-like systems
+    LOGERROR("FileWatcher not implemented, can not start watching path " + pathName);
     return false;
     return false;
 #endif
 #endif
 }
 }
@@ -105,24 +171,29 @@ void FileWatcher::StopWatching()
         file.Close();
         file.Close();
         if (fileSystem_)
         if (fileSystem_)
             fileSystem_->Delete(dummyFileName);
             fileSystem_->Delete(dummyFileName);
-        
-        Stop();
-        
-        #ifdef WIN32
-        CloseHandle((HANDLE)dirHandle_);
-        #endif
-        
-        LOGDEBUG("Stopped watching path " + path_);
+        
+        Stop();
+        
+        #if defined(WIN32)
+        CloseHandle((HANDLE)dirHandle_);
+        #elif defined(__linux__)
+        for (HashMap<int, String>::Iterator i = dirHandle_.Begin(); i != dirHandle_.End(); ++i)
+            inotify_rm_watch(watchHandle_, i->first_);
+        dirHandle_.Clear();
+        #endif
+        
+        LOGDEBUG("Stopped watching path " + path_);
         path_.Clear();
         path_.Clear();
     }
     }
 }
 }
-
-void FileWatcher::ThreadFunction()
-{
-#if defined(WIN32) && defined(ENABLE_FILEWATCHER)
-    unsigned char buffer[BUFFERSIZE];
-    DWORD bytesFilled = 0;
-    
+
+void FileWatcher::ThreadFunction()
+{
+#if defined(ENABLE_FILEWATCHER)
+#if defined(WIN32)
+    unsigned char buffer[BUFFERSIZE];
+    DWORD bytesFilled = 0;
+    
     while (shouldRun_)
     while (shouldRun_)
     {
     {
         if (ReadDirectoryChangesW((HANDLE)dirHandle_,
         if (ReadDirectoryChangesW((HANDLE)dirHandle_,
@@ -163,12 +234,47 @@ void FileWatcher::ThreadFunction()
                     break;
                     break;
                 else
                 else
                     offset += record->NextEntryOffset;
                     offset += record->NextEntryOffset;
-            }
-        }
-    }
-#endif
-}
-
+            }
+        }
+    }
+#elif defined(__linux__)
+    unsigned char buffer[BUFFERSIZE];
+
+    while (shouldRun_)
+    {
+        int i = 0;
+        int length = read(watchHandle_, buffer, sizeof(buffer));
+
+        if (length < 0)
+            return;
+
+        while (i < length)
+        {
+            inotify_event* event = (inotify_event*)&buffer[i];
+
+            if (event->len > 0)
+            {
+                if (event->mask & IN_MODIFY || event->mask & IN_MOVE)
+                {
+                    String fileName;
+                    fileName = dirHandle_[event->wd] + event->name;
+
+                    {
+                        MutexLock lock(changesMutex_);
+                        // If we have 2 unprocessed modifies in a row into the same file, only record the first
+                        if (changes_.Empty() || changes_.Back() != fileName)
+                            changes_.Push(fileName);
+                    }
+                }
+            }
+
+            i += sizeof(inotify_event) + event->len;
+        }
+    }
+#endif
+#endif
+}
+
 bool FileWatcher::GetNextChange(String& dest)
 bool FileWatcher::GetNextChange(String& dest)
 {
 {
     MutexLock lock(changesMutex_);
     MutexLock lock(changesMutex_);

+ 20 - 9
Engine/IO/FileWatcher.h

@@ -64,12 +64,23 @@ private:
     /// Buffered file changes.
     /// Buffered file changes.
     List<String> changes_;
     List<String> changes_;
     /// Mutex for the change buffer.
     /// Mutex for the change buffer.
-    Mutex changesMutex_;
-    /// Watch subdirectories flag.
-    bool watchSubDirs_;
-    
-    // Directory handle for the path being watched
-    void* dirHandle_;
-};
-
-}
+    Mutex changesMutex_;
+    /// Watch subdirectories flag.
+    bool watchSubDirs_;
+
+#ifdef WIN32
+
+    // Directory handle for the path being watched
+    void* dirHandle_;
+
+#elif __linux__
+
+    /// HashMap for the directory and sub-directories (needed for inotify's int handles)
+    HashMap<int, String> dirHandle_;
+    /// Linux inotify needs a handle.
+    int watchHandle_;
+
+#endif
+};
+
+}