Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
dir_watch.cpp
Go to the documentation of this file.
1 /* Copyright (c) 2010 Wildfire Games
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining
4  * a copy of this software and associated documentation files (the
5  * "Software"), to deal in the Software without restriction, including
6  * without limitation the rights to use, copy, modify, merge, publish,
7  * distribute, sublicense, and/or sell copies of the Software, and to
8  * permit persons to whom the Software is furnished to do so, subject to
9  * the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include "precompiled.h"
24 
25 #include "lib/sysdep/dir_watch.h"
26 #include "lib/file/file_system.h"
27 #include "osx_sys_version.h"
28 
29 #include "lib/os_path.h"
30 #include "lib/file/file.h"
31 #include "lib/posix/posix_filesystem.h" // mode_t
32 
33 #include <CoreFoundation/CoreFoundation.h>
34 #include <CoreServices/CoreServices.h>
35 
36 
37 #include "ps/CLogger.h"
38 
39 FSEventStreamRef g_Stream = NULL;
40 
41 struct DirWatch
42 {
43  OsPath path;
44  int reqnum;
45 };
46 
47 typedef std::vector<DirWatch> DirWatchMap;
51 
53 {
54  int major = 0;
55  int minor = 0;
56  int bugfix = 0;
57 
58  GetSystemVersion( major, minor, bugfix);
59 
60  if ((major == 10 && minor >= 7) || major >= 11)
61  return true;
62 
63  return false;
64 }
65 
66 #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
67 #else
68  #define kFSEventStreamCreateFlagFileEvents 0x00000010
69  #define kFSEventStreamEventFlagItemIsFile 0x00010000
70  #define kFSEventStreamEventFlagItemRemoved 0x00000200
71  #define kFSEventStreamEventFlagItemRenamed 0x00000800
72  #define kFSEventStreamEventFlagItemCreated 0x00000100
73  #define kFSEventStreamEventFlagItemModified 0x00001000
74 #endif
75 
76 static void fsevent_callback(
77  ConstFSEventStreamRef UNUSED(streamRef),
78  void * UNUSED(clientCallBackInfo),
79  size_t numEvents,
80  void *eventPaths,
81  const FSEventStreamEventFlags eventFlags[],
82  const FSEventStreamEventId UNUSED(eventIds)[] )
83 {
84  unsigned long i;
85  char **paths = (char **)eventPaths;
86 
87  for (i=0; i<numEvents; i++)
88  {
89  bool isWatched = false;
90  OsPath eventPath = OsPath(paths[i]);
91  unsigned long eventType = eventFlags[i];
92 
93  if ( eventPath.Filename().string().c_str()[0] != '.' )
94  {
95  for ( DirWatchMap::iterator it = g_Paths.begin() ; it != g_Paths.end(); ++it)
96  if ( path_is_subpath( it->path.string().c_str(), eventPath.string().c_str() ) )
97  isWatched = true;
98  }
99 
100  if ( ! isWatched )
101  return;
102 
103  OsPath filename = Path( eventPath.string().c_str() );
104 
105  if ( eventType & kFSEventStreamEventFlagItemIsFile)
106  {
107  if ( eventType & kFSEventStreamEventFlagItemRemoved )
108  g_QueuedDirs.push_back(DirWatchNotification( filename.string().c_str(), DirWatchNotification::Deleted ));
109  else if ( eventType & kFSEventStreamEventFlagItemRenamed )
110  g_QueuedDirs.push_back(DirWatchNotification( filename.string().c_str(), DirWatchNotification::Deleted ));
111  else if ( eventType & kFSEventStreamEventFlagItemCreated )
112  g_QueuedDirs.push_back(DirWatchNotification( filename.string().c_str(), DirWatchNotification::Created ));
113  else if ( eventType & kFSEventStreamEventFlagItemModified )
114  g_QueuedDirs.push_back(DirWatchNotification( filename.string().c_str(), DirWatchNotification::Changed ));
115  }
116  }
117 
118 }
119 
120 FSEventStreamRef CreateEventStream( DirWatchMap path )
121 {
122  if ( ( g_Stream == NULL ) && CanRunNotifications() )
123  {
124  CFStringRef* pathLists = (CFStringRef*)malloc( sizeof(CFStringRef*) * path.size() );
125  int index = 0;
126  for ( DirWatchMap::iterator it = path.begin() ; it != path.end(); ++it)
127  {
128  pathLists[index] = CFStringCreateWithFileSystemRepresentation( NULL, OsString(it->path).c_str());
129  index++;
130  }
131  CFArrayRef pathsToWatch = CFArrayCreate(NULL, (const void **)pathLists, index, NULL);
132 
133  FSEventStreamContext *callbackInfo = NULL;
134 
135  FSEventStreamRef stream = FSEventStreamCreate(NULL, &fsevent_callback, callbackInfo, pathsToWatch,
136  kFSEventStreamEventIdSinceNow, 1.0, kFSEventStreamCreateFlagFileEvents );
137 
138  CFRelease( pathsToWatch );
139  free( pathLists );
140 
141  FSEventStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
142  if (!FSEventStreamStart(stream))
143  debug_warn(L"event_loop FSEventStreamStart failed!");
144  else
145  return stream;
146  }
147  return NULL;
148 }
149 
151 {
152  if ( g_Stream != NULL )
153  {
154  FSEventStreamStop(g_Stream);
155  FSEventStreamInvalidate(g_Stream);
156  FSEventStreamRelease(g_Stream);
157 
158  g_Stream = NULL;
159  }
160 }
161 
162 
163 Status dir_watch_Add(const OsPath& path, PDirWatch& dirWatch)
164 {
165  PDirWatch tmpDirWatch(new DirWatch);
166  dirWatch.swap(tmpDirWatch);
167  dirWatch->path = path;
168  dirWatch->reqnum = 0;
169  g_Paths.push_back( *dirWatch );
170 
171  bool alreadyInsideRootPath = false;
172  for ( DirWatchMap::iterator it = g_RootPaths.begin() ; it != g_RootPaths.end(); ++it)
173  {
174  if ( path_is_subpath( path.string().c_str(), it->path.string().c_str() ) )
175  alreadyInsideRootPath = true;
176  }
177 
178  if ( !alreadyInsideRootPath )
179  {
181  g_RootPaths.push_back( *dirWatch );
182  }
183 
184  return INFO::OK;
185 }
186 
188 {
189  if ( g_Stream == NULL )
190  {
192  }
193  else
194  {
195  for ( DirWatchNotifications::iterator it = g_QueuedDirs.begin() ; it != g_QueuedDirs.end(); ++it)
196  notifications.push_back(DirWatchNotification( *it ));
197 
198  g_QueuedDirs.clear();
199  }
200 
201  return INFO::OK;
202 }
static DirWatchMap g_Paths
Definition: dir_watch.cpp:48
std::vector< DirWatchNotification > DirWatchNotifications
Definition: dir_watch.h:84
void GetSystemVersion(int &major, int &minor, int &bugfix)
#define UNUSED(param)
mark a function parameter as unused and avoid the corresponding compiler warning. ...
Path Filename() const
Definition: path.h:158
const Status OK
Definition: status.h:386
#define kFSEventStreamEventFlagItemRenamed
Definition: dir_watch.cpp:71
#define kFSEventStreamEventFlagItemModified
Definition: dir_watch.cpp:73
bool CanRunNotifications()
Definition: dir_watch.cpp:52
std::map< int, PDirWatch > DirWatchMap
LIB_API Status dir_watch_Add(const OsPath &path, PDirWatch &dirWatch)
start watching a single directory for changes.
Definition: dir_watch.cpp:28
FSEventStreamRef CreateEventStream(DirWatchMap path)
Definition: dir_watch.cpp:120
#define kFSEventStreamCreateFlagFileEvents
Definition: dir_watch.cpp:68
static DirWatchNotifications g_QueuedDirs
Definition: dir_watch.cpp:50
#define kFSEventStreamEventFlagItemIsFile
Definition: dir_watch.cpp:69
shared_ptr< DirWatch > PDirWatch
Definition: dir_watch.h:32
Definition: path.h:75
static DirWatchMap g_RootPaths
Definition: dir_watch.cpp:49
#define kFSEventStreamEventFlagItemRemoved
Definition: dir_watch.cpp:70
const String & string() const
Definition: path.h:123
LIB_API Status dir_watch_Poll(DirWatchNotifications &notifications)
return all pending directory watch notifications.
Definition: dir_watch.cpp:33
Path OsPath
Definition: os_path.h:31
i64 Status
Error handling system.
Definition: status.h:171
bool path_is_subpath(const wchar_t *s1, const wchar_t *s2)
is s2 a subpath of s1, or vice versa? (equal counts as subpath)
Definition: path.cpp:51
FSEventStreamRef g_Stream
Definition: dir_watch.cpp:39
static void fsevent_callback(ConstFSEventStreamRef streamRef, void *clientCallBackInfo, size_t numEvents, void *eventPaths, const FSEventStreamEventFlags eventFlags[], const FSEventStreamEventId eventIds[])
Definition: dir_watch.cpp:76
void DeleteEventStream()
Definition: dir_watch.cpp:150
#define debug_warn(expr)
display the error dialog with the given text.
Definition: debug.h:324
#define kFSEventStreamEventFlagItemCreated
Definition: dir_watch.cpp:72
static std::string OsString(const OsPath &path)
Definition: os_path.h:42